http://scrapy-chs.readthedocs.io/zh_CN/latest/intro/tutorial.html#id5*
参考书籍 《精通Scrapy网络爬虫》
网络爬虫指的是在互联网上进行自动爬取网站内容的信息得程序,也被称作网络蜘蛛和网络机器人
基本得爬取流程为:
简介:
Scrapy使用python语言基于Twisted框架编写得开源得网络爬虫框架,目前支持python2.7及python3.4+
安装
pip install scrapy //安装中如果系统提示缺乏依赖文件 pip install wheel //这是一个Twisted的依赖 pip install C:\Users\10338\Downloads\Twisted-17.9.0-cp36-cp36m-win_amd64.whl
http://books.toscrape.com/
利用shell使用命令行进行创建
//scrapy startproject 项目名称 scrapy startproject book_spider
from scrapy import cmdline cmdline.execute("scrapy crawl LG_Spider -o LG_Spider.csv".split())
编写代码(在pycharm中添加scrapy工程)
import scrapy class BooksSpider(scrapy.Spider): #定义一个爬虫 name = "books" #定义爬虫的起点 start_urls = ['http://books.toscrape.com/'] def parse(self, response): #提取数据 #获取每一本书的信息都在标签 class=“b-all-content cf” #利用CSS的方法找到所有的元素,并一次迭代 for book in response.css('article.product_pod'): book_name = book.xpath('./h3/a/@title').extract_first() book_price = book.xpath('p.price_color::text').extract_first() yield {'book_name':book_name, 'book_price':book_price, } #提取链接 next_url = response.css('ul.pager li.next a::attr(href)').extract_first() if next_url: next_url = response.urljoin(next_url) yield scrapy.Request(next_url,callback=self.parse)
extract.frist()返回字符串 extract 返回数组
name 属性:在一个scrapy中可能存在多个爬虫,name属性是爬虫的唯一的区分。
start_urls属性:爬虫要从某个页面开始爬取,也就是起始抓取点。
parse:一个页面抓取成功后,Scrapy会回掉一个我们指定的页面解析函数(默认的就是parse方法 )。
attr()jQuery。返回被选元素的属性值
scrapy crawl books -o books.csv
获取的信息将会保存在books.csv的文件中。
用来整理整个系统的数据流处理。触发事件
用来接受引擎发过来的请求,压入队列,并在引擎再次请求的时候返回,可以认为是一个URL的优先队列,由他来决定下一个要抓取的网址是什么,同时去除重复的网址。
用于下载网页内容,并将网页的内容返回给spider下载器是建立在twisted这个高效的异步模型上的
爬虫(工农阶级)主要干活的,用于从intenet爬取信息,也就所谓的实体。用户也可以从中提取出链接,让spider继续爬取下一个网页
负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体,验证实体的有效性,清除不需要的信息,当页面页面爬虫解析后,将被发送到项目管道,并经过几个特定的次序的处理数据。
位于Scrapy引擎和下载器之间框架,主要用处处理Scrapy引擎于下载器之间的请求和响应
介于Scrapy引擎和Spider之间的框架,主要是处理Scrapy引擎与Spider之间的请求和响应
介于Scrapy引擎和调度器之间的框架,从Scrapy引擎发送到调度的请求和响应
1.引擎从调度器上去取出一个链接(URL)用于接下来的爬取
2.引擎把URL封装成一个请求(request)传给下载器
3.下载器把资源下载下来,并封装成应答包(response)
4.爬虫解析response
5.解析出实体(Item),则交给实体管道进行进一步的处理
6.解析出的是链接(URL),则把URL交给调度器等待抓取
request(url[,callback,method='GET',headers,boday,cookies,meta,encoding='utf-8',priority=0,dont_filter =false,errback])
~url :请求地址
~callback:页面解析函数,callback类型,request对象请求的页面下载 完成后,有该参数指定的页面解析被调用,未被调用时spider默认调用parse方法。
~method:HTTP的请求方法,默认为GET方法。
~headers:HTTP请求的头部字典,dict类型,例如{‘A':'a','B':'b'}如果内部某项的值为NONE,就表明不发送该项的头部,例如:{‘C’:None},禁止发送C
~body:HTTP请求的正文,bytes或str类型
~cookies:信息字典,dict类型
~meta:request的元数据字典,meta标签永远位于head元素的内部。头部的元信息——用来描述信息的结构,语法,用途及用法等等‘。
~encoding:编码格式。
~priority:设置请求的优先级。
~dont_filter:默认情况下值为False,值个更改为True可以请求避免被过滤,强制下载。
~errback:错误返回。
import scrapy request = scrapy.Request('地址') request = scrapy.Request('地址',callback=self.parseItem)
页面下载完成后,搞一个Response的子类对象出来,子类有TextResponse、HtmlResponse、XmlResponse,由于通常都是搞网页玩,所以一般使用HtmlResponse,注意TextResponse是HtmlResponse和XmlResponse的父类。
~url:HTTP响应的url地址,str类型。
~status:HTTP响应的状态码。
~headers:HTTP响应的头,dict类型。
~body:HTTP响应正文,bytes里类型
~text:文本形式正文向应。
~encoding:编码格式。
~request :产生该HTTP响应的Request的对象。
~meta:即response.request.meta,构造Request对象时可以取出response.meta信息。
~selector: Selector对象 用于在response中提取数据(选择器)、
~Xpath(query):使用xpath在response中提取数据,实际上是response.selector.xpath方法的快捷方式。
~css(query):使用CSS选择器在response中数据提取。实际为response.selector.css方法的快捷方式。
~urljoin:用于构造绝对url.当传入url参数四一个相对的地址时,根据response.url计算出相应的绝对的url.
常用的方法为:css xpath 进行数据的提取,利用urljoin进行构造绝对的url.
开发的四个步骤:
1.继承scrapy.Spider
2.为Spider取名
3.设置起始爬取点。
4.实现页面解析函数。
Scrapy框架提供了一个Spider基类,。
在Spider基类中实现了:
1.供Scrapy引擎调用的接口
2.供用户使用的实用工具函数
3。供用户访问的属性。
一个项目中可以实现多个spider,name属性时区分这些工作的小爬爬的唯一属性。在执行scrapy crawl时就会用到这个标识了
Spider爬去的起始网页,start_urls 通常是一个列表,其中放入所有得爬取点url。
页面解析函数,也就是构造Request对象时通过callback参数指定的回掉函数(或者parse方法)。需要完成的工作:
1.使用选择器提取页面中的数据,数据封装(Item或dict)提交给Scrapy引擎
2.使用选择器或者LinkExtractor提取页面中的链接,用其构造新的request对象并提交给Scrapy引擎
常用的处理HTML页面解析的模块
~BeatifulSoup
~LXML
Scrapy结合了两者实现了Selector类,使用先通过Xpath或者CSS选择器选中页面下的数据,然后提取
创建Selector对象时,可将页面的HTML文档字符串传递给Selector构造器的方法的text参数数;也可以利用Response对象构造Selector对象将其传递给Selector构造器方法的response参数。
利用Xpath和css方法
由于Selenium使用xpath定位时采用遍历页面的方式,在性能上采用CSS选择器的方式更优。xpath虽然性能指标比差,但是在了浏览器中有比较好的插件支持,定位元素比较方便,对于性能要求严格的可以交替使用。
Xpath:
xpath使用路径表达式在xml文档中进行导航
xpath包含一个标准的函数库
xpath是XSLT(将xsl转换,xsl是可扩展样式表语言)中的主要元素
xpath是一个W3C标准(web的技术标准)
在xpath中,有七种类型的节点:元素、属性、文本、命名空间、处理指令、注释一级文档(根节点)。XML文档是被作为树来对待的,树的根被称为文档节点或者根节点。
xpath和css方法返回一个SelectorList对象,其中包含每个被选中部分对应的Selector对象,SelelctorList支持列表接口,可以使用for语句访问其中的每一个Selector对象:
for sel in selector_list: print(sel.xpath('./text()'))
selector_list对象也有xpath和css方法,调用他们的行为是:以接受到的参数分别调用其中每一个Selector对象的xpath和CSS方法,并将其所有的结果收到一个新的SelectorLiist对象返回给用户eg:
>>>selector_list.xpath('./text()') [, ]
调用Selector和SelectorList对象的方法:extract()、re()、extract_first()、re_first()(后两个为SelectorList专有)
1.extract()方法
调用Selector对象的extract方法将返回选中内容的Unicode字符串,与SelectorList对象的xpath和css类似, SelectorList对象的extract方法内部会调用其每个Selector对象的extract方法,并把所有的结果收集到一个列表返回给用户.
2.extract_first()
该方法放回其中第一个Selector对象调用extract方法的结果。在selectorList对象只包含一个Selector对象时调用该方法,直接提取出Unicode字符串而不是列表。
3.re和re_first
利用正则表达式提取内容的某部分,可以使用re方法,re_first方法同样返回其中的第一个Selector对象调用re方法的结果。
其实在应用过程中,几乎不需要手动创建Selector对象,在第一次访问一个Response对象的selector属性时,Response对象内部会以自身为参数自动创建Selector对象,并将该Selector对象缓存,方便下次使用。
知识点补充:@propety 属性修饰
例子:
class Student(object): def __init__(self,name;score): self.name = name self.score = score @property def score(self): return self.__score @score.setter def score(self,score): if score < 0 or score > 1000: raise ValueError('invaid score') self.__score = score
注意啦:第一个Score(self)是get 方法,用@property装饰,第二个score(self,score)是set方法,用@score.setter装饰,@score.setter是前一个@property装饰后的副厂品。
score属性设置。
>>> s = Student('Bob', 59) >>> s.score = 60 >>> print s.score 60 >>> s.score = 1000 Traceback (most recent call last): ... ValueError: invalid score
XPath也就是XML路径语言(XML Path Language)
Xpath的常用基本语法
表达式 | 描述 |
---|---|
/ | 选中文档的根(root) |
. | 选中当前节点 |
.. | 选中当前节点的父节点 |
ELEMENT | 选中子节点中所有ELEMENT元素节点 |
//ELEMENT | 选中后代节点中所有ELEMENT元素节点 |
* | 选中所有元素的子节点、 |
text() | 选中所有文本子节点 |
@ATTR | 选中名字为ATTR的属性节点 |
@* | 选中所有属性节点 |
[谓语] | 谓语用来查找某个特定的节点或者包含某个特定值得节点 |
Example website
from scrapy.selector import Selector from scrapy.http import HtmlResponse response = HtmlResponse(url = 'http://www.example.com',body=body.encoding='utf8') response.xpath('/html') response.xpath('/html/head') response.xpath('/html/body/div/a') response.xpath('//a')
这部分内容查阅填充
xpath提供许多函数,例如数字、字符串、时间、日期、统计等等。
string(arg)返回参数的字字符串值。
CSS即层样式表器,其选择器是一种来确定HTML文档某部分位置的语言。CSS使用起来比xpath简单,但是不如xpath功能强。
实际上在使用CSS方法时,python库将cssselect将CSS选择器表达式翻译成xpath表达式然后调用Selector对象的Xpath方法。
CSS选择器
表达式 | 描述 | 例子 |
---|---|---|
* | 选中所有元素 | * |
E | 选中E元素 | p |
E1,E2 | 选中E1,E2元素 | div,pre |
E1>E2 | 选中E1后代元素中的E2元素 | div p |
E1+E2 | 选中E1兄弟元素中的E2元素 | p+strong |
.CLASS | 选中CLASS属性包含CLASS的元素 | .info |
#ID | 选中id属性为ID的元素 | #main |
[ATTR] | 选中包含ATTR属性的元素 | [href] |
[ATTR=VALUE] | 选中包含ATTR属性且值为VALUE的元素 | [method=post] |
[ATTR~=VALUE] | 选中ATTR属性且值包含Value的元素 | [class~=clearfix] |
E:nth-child(n) E:nth-last-child(n) | 选中E元素,且该元素必须是其父元素的(倒数)第n个子元素 | a:ntn-child(1) a:nth-last-child(2) |
E:first-child E:last-child | 选中E元素且该元素必须是其父元素的(倒数)第一个子元素 | a:first-child a:last-child |
E:empty | 选中没有子元素的E元素 | div:empy |
E:text | 选中E元素的文本节点(Text Node) |
Item基类:自定义数据类(支持字典的接口)
field类:用来描述自定义数据类包含哪些字段
自定义一个数据类,只需要继承Item,并创建一系列的Field对象的类属性
对字段赋值时,如果是内部没有的字段,这时会抛出异常。
实际上Field是Python字典的子类,可以通过键获取Field对象中的元数据
改写之前的代码
from scrapy import Item,Field class BookSpiderItem(scrapy.Item): name = Field() price = Field()
修改之前的BooksSpider,使用BookItem替代Python字典
from ..Item import BookSpiderItem class BooksSPider(scrapy.Spider): def parse(self,response): for sel in response.css('article.product_pod'): book = BookSpiderItem() book['name'] = sel.xpath('./h3/a/@title').extract_first() book['price'] = sel.css('p.price_color::text').extract_frist() yield book
在ltem中添加新的字段利用 Field()类的属性。
一项数据由Spider提交给Scrapy引擎后,可能会提交给其他组件进行处理(Item Pipline Exporter)处理,假设想传递额外的信息给处理数据的某个组件(例如,高手组件应该如何处理数据),此时可以使用Field的元数据。
进行数据处理,一个项目中可以启用多个Item Pipeline ,它的典型应用
清洗数据
验证数据的有效性
过滤重复的数据
将数据存入数据库
项目创建时会生成一个pipelines.py的文件,
(1)一个Iltem Pipleine 不需要继承特定基类、只需要实现某些特定的方法,例如 process-_Iitem open_spider close_spider
(2)一个item Pipeline 必须实现一个process_item(item,spider)方法,该方法用来处理每一项由spider爬取的数据,其中每两个参数
Item 爬取到的一项数据(Item或字典)。
Spider 爬取此项数据的spider对象。
(3)如果process_item在处理某项item返回了一项数据(Item或字典),返回的数据会递送给下一极Item pipeline继续处理。
(4)如果process_Item在处理某项item时抛出(raise)一个Droptem异常(scarpy.exceptions.DropItem),该项Item会被抛弃,不再递送给后面的Item pipeline 继续处理,也不会导出到文件,通常,我们再检测到无效数据或过滤数据时我们会抛出DropItem异常。
(5)open_spider(self,spider)
Spider打开时(处理数据之前)回调该方法,通常该方法用于再开始处理数据之前完成某些初始化工作,如链接数据库
(6)close_spider(self,Spider)
Spider关闭时(处理数据后)回调该方法,通常该方法用于处理完所有数据之后完成清理工作,如关闭数据库。
(7)from_crawlwer(cls,crawler)
创建Item Pipeline 对象时回调该类方法,通常在该方法中通过crawler.settings读取配置,根据配置创建Item pipeline对象。
在scrapy中。想要启用某个Item Pipleine 需要在配置文件settings.py中进行配置:
ITEM_PIPELINES = { 'example.piplines.priceConverPipeline':300, }
ITEM_PIPELINES是一个字典。我们把想要启用的Item Pipeline添加到这个字典中,值为0~1000的数字,数字越小越先执行。
实现
class PriceConveterPipeline(object): exchange_rate = 8.5209 def process_item(self,item,spider): price = float(item['price'][1:])*self.exchange_rate item['price']='¥%.2f'%price
代码解释Item Pipeline不需要继承特定的基类。只需要实现某些特定的方法例如process_item、oepn_spider、close_spider.
上述的实现方法很简单将书籍的英镑价格转换为浮点数乘以汇率并保留两位小数,然后赋值给item中的price字段,最后返回被处理过的item。
可以看出,process_item在处理某项item时返回了一项数据(Item或者字典),返回的数据会递送给下一级的process_item(如果有)继续处理
过滤重复的数据,处理重复的数据,代码如下:
from scrapy.exceptions import DropItem class DuplicationPipeline(object): def __init__(self): self.book_set = set() def process_item(self,item,spider): name = item['name'] if name in self.book_set: raise DropItem("Duplicate book found: %s"%item) self.book_set.add(name) return item
增加构造器的方法,在其中初始化用于对书名去重的集合。
在process_item 方法中,先取出item的name字段,检查书名是否已经在集合book_set中,如果存在,就是重复数据,抛出DropItemy异常,将item抛弃,否则将item的name字段存入集合返回item.
把数据存放到某种数据库中,可以通过实现Item Pipeline完成
例子:
from scrapy.item import Item import pymongo class MongoDBPipeline(object): DB_URI = 'mongodb://loaclhost:27017/' DB_NAME = 'scrapy_data' def open_spieder(self,spider): self.client = pymongo.MongoClient(self.DB_URI) self.db = self.client[self.DB_NAME] def close_spider(self,spider): self.client.close() def process_item(self,item,spider): collection = self.db[spider.name] post = dict(item)if isinstance(item,item)else item collection.insert_one(post) return item
代码解释:
在类属性中定义两个常量:DB_URI 数据库的URI地址
DB_NAME 数据库的名字
spider爬取时数据库的连接只需要执行一次,应该在开始数据处理之前连接数据库,并且在处理完数据库之后关闭数据库实现open_spider(spider)和close_spider(spider)。
在process_item中实现MongoDB数据库的写入,使用self.db和spider.name获取集合collection,然后将数据插入集合,集合的insert_one方法需要传入一个字典对象,不能时item对象,因此在使用前对item的类型进行判断,如果时item对象就转换为字典。
在settings.py文件中启用MongoDBPipelien:
ITEM_PIPELINES = { 'example.pipelines.PriceConverterPipeline':300 'example.pipelines.MongoDBPipline':400 }
在爬取时,页面中会存放很多的其他网页的链接信息,有时我们需要提取这些信息,一般使用的提取方法有Selector和LinkExtractor两种方法
1.Selector 因为链接也是页面中的数据,所以使用提取数据相同的方法就可以了,在提取少量的数据的时候,链接的提取方法比较简单,使用Selector也就是足够了。
2.LinkExtractor scarpy提供了一个专门的用于提取链接的类LinkExtractor,在提取大量链接或提取规则比较复杂时,使用Link Extractor比较方便。
class BooksSpider(scrapy.SPider): def parse(self,reponse): #提取链接 #下一页的url信息在ul.pager>li>a里面 next_url = response.css('ul.pager.li.next.a::attr(href)').extract_first() if next_url: #如果找到下一页的绝对路径构造新的response对象 next_url = response.urljoin(next_url) yield scrapy.Request(next_url,callback=self.parse)
from scrapy.linkExtractors import LinkExtractor class BooksSpider(scarpy.Spider): def parse(self,reponse): le = LinkExtractor(restrict_css='ul.pager li.next') links = le.extract_links(reponse) if links: next_url = links[0].url yield scrapy.Request(next_url,callback=self.parse)
导入LinkExtractor,它位于scrapy.linkExtractors模块
创建一个linkExtarctor对象,使用一个或者多个构造器参数描述提取规则,这里传递给restrict_css参数一个CSS选择器表达方式。他描述出下一页链接所在的区域下。
调用LinkExtarctor对象的extract_Links方法传入一个Response对象,该方法依靠创建的对象时所描述的提取规则在Response对象所包含的页面中提取链接,最终返回一个列表,其中每个元素都是一个Link对象,也就是提取到的一个链接,最终返回一个列表,每个元素都是一个Link对象。
由于页面的提取一页的下一个链接只有一个,因此用Links[0]获取到的信息,因此用Links[0]获取Link对象,Link对象的url属性便是链接页面的绝对URL地址(无须再调用response.urljoin方法),在用Request构造并进行提交。
学习使用LinkExtractor的构造器参数描述提取规则
<<<<<