爬虫程序的流程
下载页面-->提取页面中的数据-->提取页面中的链接-->URL去重
Scrapy简介及安装
在任意操作系统下,可以使用pip安装Scrapy,例如:
$pip install scrapy
为确认Scrapy已安装成功,首先在Python中测试能否导入Scrapy模块:
>>> import scrapy
>>> scrapy.version_info
(1, 3, 3)
通过了以上两项检测,说明Scrapy安装成功了。Scrapy安装过程中可能会遇到一些问题,可能还要
安装win32api、lxml等,应根据报错内容百度相应的方法可以解决,也有可能环境缺少安装包,安装即可
编写Scrapy爬虫
爬取http://books.toscrape.com网站中取书籍信息,这样的书籍列表页面一共有50页,每页有20本书,第一个例子应尽量简单。我们下面以这个项目仅爬取所有图书(1000本)的书名和价格信息。
1.创建项目:在命令行输入scrapy startproject example
使用tree查看目录结构:tree
2.分析页面,一般用xpath或者css得到元素
3.实现Spider
实现爬虫的Python文件应位于exmaple/spiders目录下,在该目录下创建 新文件book_spider.py。然后,在book_spider.py中实现爬虫BooksSpider,
代码如下:
import scrapy
class BooksSpider(scrapy.Spider):
name = "books"
start_urls = ['http://books.toscrape.com/']
def parse(self, response):
for book in response.css('article.product_pod'):
name = book.xpath('./h3/a/@title').extract_first()
price = book.css('p.price_color::text').extract_first()
yield {'name':name,
'price': price,
}
next_url = response.css('ul.pager li.next a::attr(href)').extract_first()
print(next_url)
if next_url:
next_url = response.urljoin(next_url)
yield scrapy.Request(next_url, callback=self.parse)
上述代码注解:
name属性
一个Scrapy项目中可能有多个爬虫,每个爬虫的name属 性是其自身的唯一标识,在一个项目中不能有同名的爬虫,本例中的爬虫取名为'books',这个是在运行时需要用到。
start_urls属性
一个爬虫总要从某个(或某些)页面开始爬取,我们称 这样的页面为起始爬取点,start_urls属性用来设置一个 爬虫的起始爬取点。在本例中只有一个起始爬取 点'http://books.toscrape.com'。
parse方法
当一个页面下载完成后,Scrapy引擎会回调一个我们指定的页面解析函数(默认为parse方法)解析页面。一个页面解析函数通常需要完成以下两个任务:
提取页面中的数据(使用XPath或CSS选择器)。
提取页面中的链接,并产生对链接页面的下载请 求。
4.运行爬虫,scrapy crawl books -o books.csv
可能会错写报错,需要在setting文件中设置ROBOTSTXT_OBEY = False,设置robot协议不遵守
Scrapy框架结构及工作原理
上几种对象在框架中的流动过程。
● 当SPIDER要爬取某URL地址的页面时,需使用该URL构造一 个Request对象,提交给ENGINE(图2-1中的1)。
● Request对象随后进入SCHEDULER按某种算法进行排队,之 后的某个时刻SCHEDULER将其出队,送往DOWNLOADER(图 2-1中的2、3、4)。
● DOWNLOADER根据Request对象中的URL地址发送一次HTTP 请求到网站服务器,之后用服务器返回的HTTP响应构造出一个 Response对象,其中包含页面的HTML文本(图2-1中的5)。
● Response对象最终会被递送给SPIDER的页面解析函数(构造 Request对象时指定)进行处理,页面解析函数从页面中提取数 据,封装成Item后提交给ENGINE,Item之后被送往ITEM PIPELINES进行处理,最终可能由EXPORTER(图2-1中没有显 示)以某种数据格式写入文件(csv,json);另一方面,页面解 析函数还从页面中提取链接(URL),构造出新的Request对象提 交给ENGINE(图2-1中的6、7、8)。
Request对象
构造函数:Request(url[,callback, method='GET',headers,body,cookies,meta, encoding='utf8',priority=0, dont_filter=False,errback])
下面依次介绍这些参数。
●url(必选)
请求页面的url地址,bytes或str类型, 如'http://www.python.org/doc'。
●callback
页面解析函数, Callable类型,Request对象请求的页面 下载完成后,由该参数指定的页面解析函数被调用。如 果未传递该参数,默认调用Spider的parse方法。
●method
HTTP请求的方法,默认为'GET'。
●headers
HTTP请求的头部字典,dict类型,例如{'Accept': 'text/html', 'User-Agent':Mozilla/5.0'}。如果其中某项的值 为None,就表示不发送该项HTTP头部,例如{'Cookie': None},禁止发送Cookie。
●body
HTTP请求的正文,bytes或str类型。
●cookies
Cookie信息字典,dict类型,例如{'currency': 'USD', 'country': 'UY'}。
●meta
Request的元数据字典,dict类型,用于给框架中其他组 件传递信息,比如中间件Item Pipeline。其他组件可以使 用Request对象的meta属性访问该元数据字典 (request.meta),也用于给响应处理函数传递信息,详 见Response的meta属性。
●encoding
url和body参数的编码默认为'utf-8'。如果传入的url或 body参数是str类型,就使用该参数进行编码
●priority
请求的优先级默认值为0,优先级高的请求优先下载
●dont_filter
默认情况下(dont_filter=False),对同一个url地址多次 提交下载请求,后面的请求会被去重过滤器过滤(避免 重复下载)。如果将该参数置为True,可以使请求避免 被过滤,强制下载。例如,在多次爬取一个内容随时间 而变化的页面时(每次使用相同的url),可以将该参数 置为True。
●errback
请求出现异常或者出现HTTP错误时(如404页面不存 在)的回调函数
在实际应用中,我们几乎只调用Request的构造器创建对象,但也可以 根据需求访问Request对象的属性,常用的有以下几个:
●url
●method
●headers
●body
●meta
Response对象
Response对象用来描述一个HTTP响应,Response只是一个基类,根据 响应内容的不同有如下子类: ● TextResponse
● HtmlResponse
● XmlResponse
当一个页面下载完成时,下载器依据HTTP响应头部中的Content-Type 信息创建某个Response的子类对象。我们通常爬取的网页,其内容是 HTML文本,创建的便是HtmlResponse对象,其中HtmlResponse和 XmlResponse是TextResponse的子类。实际上,这3个子类只有细微的差 别,这里以HtmlResponse为例进行讲解。
下面介绍HtmlResponse对象的属性及方法。
● url
HTTP响应的url地址,str类型。
● status
HTTP响应的状态码,int类型,例如200,404。 ● headers HTTP响应的头头部,类字典类型,可以调用get或getlist 方法对其进行访问,例如:
response.headers.get('Content-Type') response.headers.getlist('Set-Cookie')
● body
HTTP响应正文,bytes类型。
● text
文本形式的HTTP响应正文,str类型,它是由 response.body使用response.encoding解码得到的,即
reponse.text = response.body.decode(response.encoding)
● encoding HTTP响应正文的编码,它的值可能是从HTTP响应头部 或正文中解析出来的。
● request 产生该HTTP响应的Request对象。
● meta 即response.request.meta,在构造Request对象时,可将要 传递给响应处理函数的信息通过meta参数传入;响应处 理函数处理响应时,通过response.meta将信息取出。
● selector Selector对象用于在Response中提取数据(选择器相关话 题在后面章节详细讲解)。 ● xpath(query) 使用XPath选择器在Response中提取数据,实际上它是 response.selector.xpath方法的快捷方式(选择器相关话题 在后面章节详细讲解)。
● css(query) 使用CSS选择器在Response中提取数据,实际上它是 response.selector.css方法的快捷方式(选择器相关话题在 后面章节详细讲解)。
● urljoin(url) 用于构造绝对url。当传入的url参数是一个相对地址时, 根据response.url计算出相应的绝对url。例如, response.url为http://www.example.com/a,url为 b/index.html,调用response.urljoin(url)的结果为 http://www.example.com/a/b/index.html
实际上,对于起始爬取点的下载请求是由Scrapy引擎调用 Spider对象的start_requests方法提交的,由于BooksSpider类没有实 现start_requests方法,因此引擎会调用Spider基类的start_requests 方法。 ● 在start_requests方法中,self.start_urls便是我们定义的起始爬 取点列表(通过实例访问类属性),对其进行迭代,用迭代出的 每个url作为参数调用make_requests_from_url方法。 ● 在make_requests_from_url方法中,我们找到了真正构造 Reqeust对象的代码,仅使用url和dont_filter参数构造Request对 象。 ● 由于构造Request对象时并没有传递callback参数来指定页面解 析函数,因此默认将parse方法作为页面解析函数。此时 BooksSpider必须实现parse方法,否则就会调用Spider基类的parse 方法,从而抛出NotImplementedError异常(可以看作基类定义了 一个抽象接口)。 ● 起始爬取点可能有多个,start_requests方法需要返回一个可迭 代对象(列表、生成器等),其中每一个元素是一个Request对 象。这里,start_requests方法被实现成一个生成器函数(生成器对 象是可迭代的),每次由yield语句返回一个Request对象。 由于起始爬取点的下载请求是由引擎调用Spider对象的start_requests方
法产生的,因此我们也可以在BooksSpider中实现start_requests方法(覆盖 基类Spider的start_requests方法),直接构造并提交起始爬取点的Request对 象。在某些场景下使用这种方式更加灵活,例如有时想为Request添加特定 的HTTP请求头部,或想为Request指定特定的页面解析函数。
到此,我们介绍完了为爬虫设定起始爬取点的两种方式:
● 定义start_urls属性。 ● 实现start_requests方法
实现页面解析函数
页面解析函数也就是构造Request对象时通过callback参数指定的回调 函数(或默认的parse方法)。页面解析函数是实现Spider中最核心的部 分,它需要完成以下两项工作: ● 使用选择器提取页面中的数据,将数据封装后(Item或字典) 提交给Scrapy引擎。 ● 使用选择器或LinkExtractor提取页面中的链接,用其构造新的 Request对象并提交给Scrapy引擎(下载链接页面
使用Selector提取数据
Selector类的实现位于scrapy.selector模块,创建Selector对象时,可将 页面的HTML文档字符串传递给Selector构造器方法的text参数:
from scrapy.selector import Selector
selector=Selector(text=text)
也可以使用一个Response对象构造Selector对象,将其传递给Selector构 造器方法的response参数:
from scrapy.selector import Selector
from scrapy.http import HtmlResponse
response=HtmlResponse(url='http://www.example.com', body=body) selector=Selector(response=response)
选中数据
selector_list=selector.xpath('//h1')
提取数据
调用Selector或SelectorLis对象的以下方法可将选中的内容提取:
● extract()
● re()
● extract_first() (SelectorList专有)
● re_first() (SelectorList专有
XPath
XPath语法
/ 选中文档根
. 选中当前节点
.. 选中当前节点的父节点
ELEMNT 选中节点中所有ELEMNT元素节点
//ELEMNT 选中后代节点中所有的ELEMENT节点
* 选中所有元素
text() 选中所有文本子节点
@ATTR 选中名为ATTR的属性节点
@* 选中所有节点
[谓语] 谓语用来查找某个节点或者包含某个特定值的节点
常用函数
string(arg)
possition()
last()
contains(str2,str2)
css选择器
了CSS选择器的一些基本语法
* 选中所有元素
E 选中E元素
E1,E2 选中E1和E2元素
E1 E2 选中E1后代元素中的E2
E1>E2 选中E1中的元素E2
.class 选中class属性包含class的元素
#ID 选中id属性为ID的元素
[ATTR] 选中包含ATTR属性的元素
[ATTR=VALUE] 选中包含ATTR属性且属性值为VALUE的元素
[ATTR~=VALUE] 选中包含ATTR属性且包含VALUE的元素
E:nth-child(n) 选中E元素,且改元素必须是其父元素的第n个子元素
E:nth-last-child(n) 选中E元素,且改元素必须是其父元素的倒数第n个子元素
E:frist-child 选中E元素,且改元素必须是其父元素的第一个元素
E:last-child 选中E元素,且改元素必须是其父元素的倒数第一个元素
E:empty 选中没有子元素的E元素
E::text 选中E元素的文本节点
使用Item封装数据
Scrapy提供了以下两个类,用户可以使用它们自定义数据类(如书籍 信息),封装爬取到的数据: Item基类 自定义数据类(如BookItem)的基类。
● Field类 用来描述自定义数据类包含哪些字段(如name、price 等)。 自定义一个数据类,只需继承Item,并创建一系列Field对象的类属性 (类似于在Django中自定义Model)即可。以定义书籍信息BookItem为 例,它包含两个字段,分别为书的名字name和书的价格price,代码如下:
from scrapy import Item,Field
class BookItem(Item):
name=Field()
price=Field()
使用Item Pipeline处理数据
以下是Item Pipeline的几种典型应用:
● 清洗数据。
● 验证数据的有效性。
● 过滤掉重复的数据。
● 将数据存入数据库。
在创建一个Scrapy项目时,会自动生成一个pipelines.py文件,它用来 放置用户自定义的Item Pipeline,在example项目的pipelines.py中实现 PriceConverterPipeline,代码如下: