python爬虫工作原理(python爬虫技术深入理解原理)
Python爬虫看看哪个城市最热
哪个城市最热?
现如今,我大天朝举国上下都处于一种高温的状态。那么这里我们也来蹭一蹭热度,来写个小爬虫,看看全国哪个城市最热。东西虽然小,但是比起那些所谓“高大上”的教程来说,这篇文章比较脚踏实地,不玩虚的,你看完后一定可以学会的。
准备工作:
在做爬虫之前,首先对爬虫的解题路径做个简单概述。一个爬虫工作分为两步骤:
第一步:把你需要爬取的那个网页的数据全部拉下来。
第二步是把从网站上拉下来的数据进行过滤,把你需要的提取出来,把不需要的给过滤掉。这就是爬虫的工作原理。
我们要爬取的网站数据不是特别大,复杂度也不高。因此为降低大家学习爬虫的难度。我们不上那些很复杂的框架,比如Scrapy,而是直接使用一个简单又好用的第三方库帮我们完成爬虫任务,那就是requests库。requests库是一个专门用来从网站上拉取数据的库,也就对应了我们爬虫工作的第一步——从网站上拉取数据。这个库的使用,后面我们会讲到,如果您还没有安装这个库,那么可以通过以下命令进行安装:pip install requests。如果您使用的是Linux或者Mac OS系统,那么最好在前面加上一个sudo pip install requests,不然很容易会因为权限问题而安装失败。
在使用requests库把数据拉取下来以后,接下来的工作就是对这些数据进行分析和过滤了。这时候我们使用的一个第三方库是beautifulsoup4。这个库是专门用来解析网页数据的。并且为了使接下效率更高,一般我们推荐使用lxml来作为解析的引擎。因此,要使用beautifulsoup库,要通过以下命令来安装:
# 安装 lxmlpip install lxml# 安装 beautifulsoup4pip install bs4
中国天气网分析:
这里我们需要爬取的网站是:中国天气网her.com.cn/textFC/hb.shtml#。我们做任何爬虫之前,都需要对这个网站进行详细的分析,一定要达到了如指掌的境地。不然后面很容易在数据过滤的时候会出现一个问题,这也是所有做爬虫需要注意的地方。我们这里就跟大家分析一下中国天气网的网站:
在以上这个中国天气网的页面中。我们可以看到,他是通过区域的方式把全国个大省份和城市进行区分的,有:华北、东北、华东、华中、华南、西北、西南、港澳台。在每个区域下面,都是通过表格的形式把属于该区域下的所有省份以及城市的天气信息列出来。因此我们要做的是,首先找到所有的区域的链接,然后再在每个区域的链接下把这个区域下的所有的城市天气信息爬出来就可以了。?
我们已经知道只要拿到了所有的区域链接,就可以在这个链接的网页中把这些城市拿到。那么如何去拿到这个区域下对应的所有城市以及他们对应的天气信息呢?这时候就需要去解析源代码了,我们点击这个页面任何一个地方,然后右键点击审查元素,打开控制台后,去寻找规则(因为这个寻找规则比较繁琐,这里就做个简单概述,如果对如何去找到相应的元素感兴趣,可以关注本公众号,会有一套详细的视频教程免费教大家查找)。我们可以看到,一个省份的城市及其天气信息,都是放在一个叫做div class='conMidtab2'的盒子中。因此我们只要找到所有的conMidtab2盒子,就找到了所有的城市。但是这个地方需要注意的是,因为在一个页面中,展示了 7 天的数据,因此如果你按照class='conMidtab2'这个规则去寻找的话,会得到从今天往后延 7 天的所有数据。这时候我们就要换一个规则了,我们经过分析(具体分析过程可以看本教程视频内容)发现一天的天气信息,是放在一个class='conMidtab'的这样一个盒子中。因此我们只要把第一个conMidtab盒子拿到,就可以拿到今天的天气数据而把其他日期的数据给过滤掉。这里要给大家上一份源代码了:
# find 方法,只会返回第一个满足条件的元素,也即只把第一个 conMidtab 返回回来,把今天的数据返回回来conMidtab = soup.find('div', class_='conMidtab')# 通过得到的这个 conMidtab,我们再调用 find_all 方法,把这个 conMidtab 元素下的所有 conMidtab2 盒子找到,得到这一天中这个区域下所有的城市conMidtab2_list = conMidtab.find_all('div', class_='conMidtab2')
也上一份图片给大家分析一下:?
我们已经分析好了如何获取城市的盒子,那么接下来就是去获取城市的天气信息了。我们继续分析(具体分析过程可以看本视频教程)后发现,在table中,前面两个tr是表头,知识用来解释数据的,我们不需要,那么真正有用的数据是从第三个tr开始的。并且第三个tr的第 1 个td展示的是该省或者直辖市的名称,从第二个td开始才是真正展示该省份下这个城市的详细天气情况了,这里上一张图片分析一下:?
并且,从第四个tr开始,下面的td全部展示的就是这个城市的信息了,而不包括省份的信息了,这里也用一张图片来说明这个问题:?
所以综合以上的分析,我们就知道,省份名称是存储在table下的第三个tr下的第 0 个td下,而城市名字则要进行判断,如果是第三个tr,那么就是在第二个td,如果是从第四个开始,那么就是存储在第 0 个td下。而最高气温也要区分,第三个tr,存储在第 5 个td下,从第四个tr开始,则存储在第 4 个td下,那么我们也可以上一份源代码,看看如何获取的:
conMidtab = soup.find('div', class_='conMidtab')
conMidtab2_list = conMidtab.find_all('div', class_='conMidtab2')for x in conMidtab2_list:
tr_list = x.find_all('tr')[2:]
province = ''
for index, tr in enumerate(tr_list):
# 如果是第 0 个 tr 标签,那么城市名和省份名是放在一起的 ? ? ? ?min = 0
if index == 0:
td_list = tr.find_all('td')
province = td_list[0].text.replace('\n', '')
city = td_list[1].text.replace('\n', '') ? ? ? ? ? ?max = td_list[5].text.replace('\n', '') ? ? ? ?else:
# 如果不是第 0 个 tr 标签,那么在这个 tr 标签中只存放城市名
td_list = tr.find_all('td')
city = td_list[0].text.replace('\n', '') ? ? ? ? ? ?max = td_list[4].text.replace('\n', '')
TEMPERATURE_LIST.append({ ? ? ? ? ? ?'city': province+city, ? ? ? ? ? ?'min': min
})
数据显示:
如果把所有数据都爬下来后,那么我们可以使用echarts-python把数据进行可视化,这部分内容比较简单,我就直接把我做的可视化的数据图给大家看下,具体的实现方式,大家可以观看我的视频教程,讲解非常详细:?
写在最后:
python爬虫需要什么基础
网页知识
html,js,css,xpath这些知识,虽然简单,但一定需要了解。 你得知道这些网页是如何构成的,然后才能去分解他们.
HTTP知识
一般爬虫你需要模拟浏览器的操作,才能去获取网页的信息
如果有些网站需要登录,才能获取更多的资料,你得去登录,你得把登录的账号密码进行提交
有些网站登录后需要保存cookie信息才能继续获取更多资料
正则表达式
有了正则表达式才能更好的分割网页信息,获取我们想要的数据,所以正则表达式也是需要了解的.
一些重要的爬虫库
url,url2
beautiul Soup
数据库
爬取到的数据我们得有个地方来保存,可以使用文件,也可以使用数据库,这里我会使用mysql,还有更适合爬虫的MongoDB数据库,以及分布式要用到的redis 数据库
爬虫框架
PySpider和Scrapy?这两个爬虫框架是非常NB的,简单的爬虫可以使用urllib与urllib2以及正则表达式就能完成,但高级的爬虫还得用这两个框架。 这两个框架需要另行安装。后面一起学习.
反爬虫
有时候你的网站数据想禁止别人爬取,可以做一些反爬虫处理操作。 打比方百度上就无法去查找淘宝上的数据,这样就避开了搜索引擎的竞争,淘宝就可以搞自己的一套竞价排名
分布式爬虫
使用多个redis实例来缓存各台主机上爬取的数据。
爬虫要学的东西还是挺多的,想把爬虫玩得666,基本就是这些知识点吧!
python分布式爬虫是什么意思
一、分布式爬虫架构
在了解分布式爬虫架构之前,首先回顾一下Scrapy的架构,如下图所示。
Scrapy单机爬虫中有一个本地爬取队列Queue,这个队列是利用deque模块实现的。如果新的Request生成就会放到队列里面,随后Request被Scheduler调度。之后,Request交给Downloader执行爬取,简单的调度架构如下图所示。
如果两个Scheduler同时从队列里面取Request,每个Scheduler都有其对应的Downloader,那么在带宽足够、正常爬取且不考虑队列存取压力的情况下,爬取效率会有什么变化?没错,爬取效率会翻倍。
这样,Scheduler可以扩展多个,Downloader也可以扩展多个。而爬取队列Queue必须始终为一个,也就是所谓的共享爬取队列。这样才能保证Scheduer从队列里调度某个Request之后,其他Scheduler不会重复调度此Request,就可以做到多个Schduler同步爬取。这就是分布式爬虫的基本雏形,简单调度架构如下图所示。
我们需要做的就是在多台主机上同时运行爬虫任务协同爬取,而协同爬取的前提就是共享爬取队列。这样各台主机就不需要各自维护爬取队列,而是从共享爬取队列存取Request。但是各台主机还是有各自的Scheduler和Downloader,所以调度和下载功能分别完成。如果不考虑队列存取性能消耗,爬取效率还是会成倍提高。
二、维护爬取队列
那么这个队列用什么来维护?首先需要考虑的就是性能问题。我们自然想到的是基于内存存储的Redis,它支持多种数据结构,例如列表(List)、集合(Set)、有序集合(Sorted Set)等,存取的操作也非常简单。
Redis支持的这几种数据结构存储各有优点。
列表有lpush()、lpop()、rpush()、rpop()方法,我们可以用它来实现先进先出式爬取队列,也可以实现先进后出栈式爬取队列。
集合的元素是无序的且不重复的,这样我们可以非常方便地实现随机排序且不重复的爬取队列。
有序集合带有分数表示,而Scrapy的Request也有优先级的控制,我们可以用它来实现带优先级调度的队列。
我们需要根据具体爬虫的需求来灵活选择不同的队列。
三、如何去重
Scrapy有自动去重,它的去重使用了Python中的集合。这个集合记录了Scrapy中每个Request的指纹,这个指纹实际上就是Request的散列值。我们可以看看Scrapy的源代码,如下所示:
import?hashlib
def?request_fingerprint(request, include_headers=None):
if?include_headers:
include_headers = tuple(to_bytes(h.lower())
for?h?in?sorted(include_headers))
cache = _fingerprint_cache.setdefault(request, {})
if?include_headers?not?in?cache:
fp = hashlib.sha1()
fp.update(to_bytes(request.method))
fp.update(to_bytes(canonicalize_url(request.url)))
fp.update(request.body?or?b'')
if?include_headers:
for?hdr?in?include_headers:
if?hdr?in?request.headers:
fp.update(hdr)
for?v?in?request.headers.getlist(hdr):
fp.update(v)
cache[include_headers] = fp.hexdigest()
return?cache[include_headers]
request_fingerprint()就是计算Request指纹的方法,其方法内部使用的是hashlib的sha1()方法。计算的字段包括Request的Method、URL、Body、Headers这几部分内容,这里只要有一点不同,那么计算的结果就不同。计算得到的结果是加密后的字符串,也就是指纹。每个Request都有独有的指纹,指纹就是一个字符串,判定字符串是否重复比判定Request对象是否重复容易得多,所以指纹可以作为判定Request是否重复的依据。
那么我们如何判定重复呢?Scrapy是这样实现的,如下所示:
def?__init__(self):
self.fingerprints = set()
def?request_seen(self, request):
fp = self.request_fingerprint(request)
if?fp?in?self.fingerprints:
return?True
self.fingerprints.add(fp)
在去重的类RFPDupeFilter中,有一个request_seen()方法,这个方法有一个参数request,它的作用就是检测该Request对象是否重复。这个方法调用request_fingerprint()获取该Request的指纹,检测这个指纹是否存在于fingerprints变量中,而fingerprints是一个集合,集合的元素都是不重复的。如果指纹存在,那么就返回True,说明该Request是重复的,否则这个指纹加入到集合中。如果下次还有相同的Request传递过来,指纹也是相同的,那么这时指纹就已经存在于集合中,Request对象就会直接判定为重复。这样去重的目的就实现了。
Scrapy的去重过程就是,利用集合元素的不重复特性来实现Request的去重。
对于分布式爬虫来说,我们肯定不能再用每个爬虫各自的集合来去重了。因为这样还是每个主机单独维护自己的集合,不能做到共享。多台主机如果生成了相同的Request,只能各自去重,各个主机之间就无法做到去重了。
那么要实现去重,这个指纹集合也需要是共享的,Redis正好有集合的存储数据结构,我们可以利用Redis的集合作为指纹集合,那么这样去重集合也是利用Redis共享的。每台主机新生成Request之后,把该Request的指纹与集合比对,如果指纹已经存在,说明该Request是重复的,否则将Request的指纹加入到这个集合中即可。利用同样的原理不同的存储结构我们也实现了分布式Reqeust的去重。
四、防止中断
在Scrapy中,爬虫运行时的Request队列放在内存中。爬虫运行中断后,这个队列的空间就被释放,此队列就被销毁了。所以一旦爬虫运行中断,爬虫再次运行就相当于全新的爬取过程。
要做到中断后继续爬取,我们可以将队列中的Request保存起来,下次爬取直接读取保存数据即可获取上次爬取的队列。我们在Scrapy中指定一个爬取队列的存储路径即可,这个路径使用JOB_DIR变量来标识,我们可以用如下命令来实现:
scrapy crawl spider -s JOB_DIR=crawls/spider
更加详细的使用方法可以参见官方文档,链接为:。
在Scrapy中,我们实际是把爬取队列保存到本地,第二次爬取直接读取并恢复队列即可。那么在分布式架构中我们还用担心这个问题吗?不需要。因为爬取队列本身就是用数据库保存的,如果爬虫中断了,数据库中的Request依然是存在的,下次启动就会接着上次中断的地方继续爬取。
所以,当Redis的队列为空时,爬虫会重新爬取;当Redis的队列不为空时,爬虫便会接着上次中断之处继续爬取。
五、架构实现
我们接下来就需要在程序中实现这个架构了。首先实现一个共享的爬取队列,还要实现去重的功能。另外,重写一个Scheduer的实现,使之可以从共享的爬取队列存取Request。
幸运的是,已经有人实现了这些逻辑和架构,并发布成叫Scrapy-Redis的Python包。接下来,我们看看Scrapy-Redis的源码实现,以及它的详细工作原理
python爬虫能干什么
python爬虫就是模拟浏览器打开网页,获取网页中想要的那部分数据。利用爬虫我们可以抓取商品信息、评论及销量数据;可以抓取房产买卖及租售信息;可以抓取各类职位信息等。
爬虫:
网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。另外一些不常使用的名字还有蚂蚁、自动索引、模拟程序或者蠕虫。
(推荐教程:Python入门教程)
通俗的讲就是通过程序去获取web页面上自己想要的数据,也就是自动抓取数据。
python爬虫能做什么?
从技术层面来说就是通过程序模拟浏览器请求站点的行为,把站点返回的HTML代码/JSON数据/二进制数据(图片、视频) 爬到本地,进而提取自己需要的数据存放起来使用。
利用爬虫我们可以获取大量的价值数据,从而获得感性认识中不能得到的信息,比如:
爬取知乎优质答案,为你筛选出各话题下最优质的内容。
抓取淘宝、京东商品、评论及销量数据,对各种商品及用户的消费场景进行分析。
抓取房产买卖及租售信息,分析房价变化趋势、做不同区域的房价分析。
爬取各类职位信息,分析各行业人才需求情况及薪资水平。
爬虫的本质:
爬虫的本质就是模拟浏览器打开网页,获取网页中我们想要的那部分数据。
如何入门 Python 爬虫
爬虫我也是接触了1个月,从python小白到现在破译各种反爬虫机制,我给你说说我的方向:
1、学习使用解析网页的函数,例如:
import?urllib.request
if?__name__?==?'__main__':
url?=?"..."
data?=?urllib.request.urlopen(url).read()?????#urllib.request.urlopen(需要解析的网址)
data?=?data.decode('unicode_escape','ignore')?#用unicode_escape方式解码
print(data)
2、学习正则表达式:
正则表达式的符号意义在下面,而正则表达式是为了筛选出上面data中的信息出来,例如:
def get_all(data):
? ?reg = r'(search.+)(" )(mars_sead=".+title=")(.+)(" data-id=")'
? ?all = re.compile(reg);
? ?alllist = re.findall(all, data)
? ?return alllist
3、将得到的结果压进数组:
if?__name__?==?'__main__':
info = []
info.append(get_all(data))
4、将数组写进excel:
import xlsxwriter
if?__name__?==?'__main__':
info = []
info.append(get_all(data))
workbook = xlsxwriter.Workbook('C:\\Users\\Administrator\\Desktop\\什么文件名.xlsx') ?# 创建一个Excel文件
worksheet = workbook.add_worksheet() ?# 创建一个工作表对象
for i in range(0,len(info)):
worksheet.write(行, 列, info[i], font)#逐行逐列写入info[i]
workbook.close()#关闭excel
一个简单的爬虫搞定,爬虫的进阶不教了,你还没接触过更加看不懂
python为什么叫爬虫
因为python的脚本特性,易于配置,对字符的处理也非常灵活,就像虫子一样灵活,故名爬虫。
Python是完全面向对象的语言。函数、模块、数字、字符串都是对象。并且完全支持继承、重载、派生、多继承,有益于增强源代码的复用性。
Python支持重载运算符和动态类型。相对于Lisp这种传统的函数式编程语言,Python对函数式设计只提供了有限的支持。有两个标准库(functools, itertools)提供了Haskell和Standard ML中久经考验的函数式程序设计工具。
扩展资料
Python的设计目标之一是让代码具备高度的可阅读性。它设计时尽量使用其它语言经常使用的标点符号和英文单字,让代码看起来整洁美观。它不像其他的静态语言如C、Pascal那样需要重复书写声明语句,也不像它们的语法那样经常有特殊情况和意外。
Python开发者有意让违反了缩进规则的程序不能通过编译,以此来强制程序员养成良好的编程习惯。
并且Python语言利用缩进表示语句块的开始和退出(Off-side规则),而非使用花括号或者某种关键字。增加缩进表示语句块的开始,而减少缩进则表示语句块的退出。缩进成为了语法的一部分。
例如if语句:python3。