1.Scrapy的命令行命令
- 创建一个Scrapy工程
终端输入:
scrapy startproject <project_name> [project_dir]
//在project_dir下创建一个Scrapy项目,如果project_dir省略的话,
//会创建一个和<project_name>同名的文件夹内,项目放在当前的文件路径下
- PyCharm 下直接运行 Scrapy
Scrapy提供了一个cmdline模块,使Python的模块直接可以运行命令行
方法如下:
在目录的顶层(非必须,主要是为了和业务代码分开
),单独创建main.py
文件.直接运行该文件, exampleName替换为你的Spider的名字 ,即 你的spider的name属性
的内容
main.py
文件内容如下
# -*- coding=utf8 -*-from scrapy import
from scrapy import cmdline
cmdline.execute("scrapy crawl exampleName".split())
2.目录分类:
* 模拟登陆
* XPath和CSS的选择器的使用
* Scrapy整体运行流程
* Python的setter和getter方法的创建
* Scrapy如何管理Request队列
* item pipeline的使用
* Request
* 验证码识别
2.1 模拟登陆
1.如何快速定位POST的数据格式
```
1.使用抓包工具进行解析
```
2.表单数据的Scrapy的数据提交和回调
def start_requests(self):
""" 登陆页面 获取xrsf """
return [Request( "https://www.zhihu.com/#signin", \
meta={'cookiejar': 1},\
callback=self.post_login\
)]
//Scrapy 通过start_requests方法返回Request的请求list来进行请求,
//通过callback参数来确定回调方法,默认的回调方法是 parser.
//如果不重写start_requests方法,那就必须要提供start_urls 类的变量,
//官方说法是start_urls如果为空才调用start_requests方法生成请Request
//但是我测试的时候是start_requests会覆盖掉start_urls的请求列表
不同回调方法官方示例:
import scrapy
class AuthorSpider(scrapy.Spider):
name = 'author'
start_urls = ['http://quotes.toscrape.com/']
def parse(self, response):
# follow links to author pages
for href in response.css('.author+a::attr(href)').extract():
yield scrapy.Request(response.urljoin(href),
callback=self.parse_author)
# follow pagination links
next_page = response.css('li.next a::attr(href)').extract_first()
if next_page is not None:
next_page = response.urljoin(next_page)
yield scrapy.Request(next_page, callback=self.parse)
def parse_author(self, response):
def extract_with_css(query):
return response.css(query).extract_first().strip()
yield {
'name': extract_with_css('h3.author-title::text'),
'birthdate': extract_with_css('.author-born-date::text'),
'bio': extract_with_css('.author-description::text'),
}
2.2XPath和CSS的选择器的使用
1.XPath和CSS的语法
详情看专门章节
2.XPath和CSS选择器
scrapy官方建议采用XPath选择器,因为CSS选择器的底层也是采用的XPath实现的.
response.css("xxx")
// 该方法返回一个类似list的SelectorList对象.
response.css('title::text').extract()
//直接获取text便签下的内容
response.css('title::text').extract_first()
和response.css('title::text')[0].extract() 是相同功能.但extract_first(),在访问的对象不存在是返回None,但后者却是直接抛出异常.
response.css('title::text').re(r'Quotes.*')
response.css('title::text').re(r'(\w+) to (\w+)')
// re( )方法可以通过正则表达式,获取需要的对象.
CSS和XPath可以相互转换
from scrapy import Selector
sel = Selector(text='<div class="hero shout"><div class="mmm">dffdf</div><time datetime="2014-07-23 19:00">Special date</time></div>')
print(sel.css('.shout').xpath('./time/@datetime').extract())
print(sel.xpath('.//div')[1].css('.mmm').extract())
输出:
['2014-07-23 19:00']
['<div class="mmm">dffdf</div>']
2.3item pipeline的使用
1 . item
item 类提供了一个类似dict的API ,用来提取/存储数据.也就是说你可以把它当做dict来用.
声明方法:
import scrapy
class Product(scrapy.Item):
name = scrapy.Field()
price = scrapy.Field()
stock = scrapy.Field()
last_updated = scrapy.Field(serializer=str)
//Field()简单理解为初始化方法
创建方法:
product = Product(name='Desktop PC', price=1000)
print product
#Product(name='Desktop PC', price=1000)
值获取:
>>> product['name']
Desktop PC
>>> product.get('name')
Desktop PC
>>> product['price']
1000
>>> product['last_updated']Traceback (most recent call last):
...
KeyError: 'last_updated'>>> product.get('last_updated', 'not set')
not set
>>> product['lala'] # getting unknown fieldTraceback (most recent call last):
...
KeyError: 'lala'
>>> product.get('lala', 'unknown field')
'unknown field'
>>> 'name' in product # is name field populated?
True
>>> 'last_updated' in product # is last_updated populated?
False
>>> 'last_updated' in product.fields # is last_updated a declared field?
True
快速赋值:
>>> product2 = Product
(product)
>>> print product2Product
(name='Desktop PC', price=1000)
>>> product3 = product2.copy()
>>> print product3Product
(name='Desktop PC', price=1000)
ItemLoaders
提供解析是快速赋值,有时间再看
2 . Item Pipeline
Pipeline类默认方法
def process_item(self, item, spider)
#这个方法是每一个parse函数(callback)都会调用(必须实现方法)
返回值是后面之中之一:包含数据的字典,Item的类,raise DropItem exception
def open_spider(self, spider)
#爬虫开始执行时调用
def close_spider(self, spider)
#爬虫结束时调用
def from_crawler(cls, crawler)
#生成这个单例类用的配置方法,必须返回一个 Crawler 实例
激活 Item Pipeline 模块
在设置模块中添加如下代码:
ITEM_PIPELINES = { 'myproject.pipelines.PricePipeline': 300,
'myproject.pipelines.JsonWriterPipeline': 800,
}
#后面的 300, 800 是调用的优先级,范围: 0 ~ 1000
* Scrapy如何管理Request队列
这有一点未弄清楚,就是分析出的URLs再次加入爬取队列的逻辑.
engine会判断返回的数据类型(item或者是Requst)分别交给item pipline或者是Scheduler将要请求的列表 详细看 4.Scrapy整体运行流程
3 . Request
只写提交表单的FormRequest
import scrapy
class LoginSpider(scrapy.Spider):
name = 'example.com'
start_urls = ['http://www.example.com/users/login.php']
def parse(self, response):
return scrapy.FormRequest.from_response(response, formdata={'username': 'john', 'password': 'secret'}, callback=self.after_login)
def after_login(self, response):
if "authentication failed" in response.body:
self.logger.error("Login failed")
return
# continue scraping with authenticated session...
4. Scrapy整体运行流程
数据流向图:
在Scrapy中,数据流被执行Engine控制着,运行方式如下:
- Engine 从Spider获取初始化的Requests.
- Engine 把Requests传递给Scheduler ,并询问下一个需要爬取得请求 Requests.
- Scheduler 返回下一个Requests 给 Engine
- Engine 把3中获得的Requests通过中间件 (
Downloader Middlewares
先调用process_request()
函数)发送给Downloader. - 当页面下载完成,Dowenloader会生成一个Response,并把这个Response通过中间件(
Downloader Middlewares
先调用process_response()
函数)发送给 Engine. - Engine 获得Dowenloader 生成的Response,然后通过中间件(
Spider Middleware
先调用process_spider_input()
函数)发送给Spider - Spider 处理这个Response,然后通过中间件(
Spider Middleware
先调用process_spider_output()
函数)返回抽取生成的items和Reqeusts(网页中需要爬取得连接生成的请求) - Engine 发送处理过的items给Item Piplines,然后发送 7中生成的请求到Scheduler并询问下一个需要爬取的请求.
- 重复步骤1 , 直到Scheduler中没有请求
4.1 中间件
Downloader Middlewares 和Spider Middleware
中间件其实就是个Python的class文件,实现对应的方法并在配置文件中声明注册即可
Downloader Middlewares 中间件 :
process_request() :如果要在爬取请求没有开始前对请求进行处理.可选这个函数
process_response():页面返回的数据统需要处理,可以选着个函数.
下载中间件使用场景:
- 需要在请求发送到Downloader之前需要处理请求
- 需要在相应被发送给spider之前改变的响应的内容
- 静默的丢弃一些请求
Spider Middleware 中间件:
process_spider_input() :如果需要在Spider没有处理Response强处理响应的话,可以调用这个函数
process_spider_output():如果需要在Spider的parse的函数解析后处理数据,可以调用这个函数
Spider Middleware 中间件使用场景:
- 处理spider的callback的返回内容 -改变/添加/移除items或Requests
- 处理开始请求
- 处理spider的异常
- call errback instead of callback for some of the requests based on response content.
激活下载中间件
在配置文件中注册:
DOWNLOADER_MIDDLEWARES = { 'myproject.middlewares.CustomDownloaderMiddleware': 543,
}
Scrapy 中有内建的中间件如下
{
'scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware': 100,
'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddlewar': 300,
'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware': 350,
'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware': 400,
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': 500,
'scrapy.downloadermiddlewares.retry.RetryMiddleware': 550,
'scrapy.downloadermiddlewares.ajaxcrawl.AjaxCrawlMiddleware': 560,
'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware': 580,
'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 590,
'scrapy.downloadermiddlewares.redirect.RedirectMiddleware': 600,
'scrapy.downloadermiddlewares.cookies.CookiesMiddleware': 700,
'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 750,
'scrapy.downloadermiddlewares.chunked.ChunkedTransferMiddleware': 830,
'scrapy.downloadermiddlewares.stats.DownloaderStats': 850,
'scrapy.downloadermiddlewares.httpcache.HttpCacheMiddleware': 900,
}
后面的数字是调用顺序.你注册的中间件会按照数值合并到系统默认的表中(也就是上边的表).
自定义下载中间件需要实现下面的一个或多个方法:
process_request(request, spider)
每一个经过下载中心的请求都会调用这个函数:返回值 可能是None, Response, Request, 或者IgnoreRequest.
* 返回 None: Scrapy会继续处理这个请求,知道请求结束
* 返回 Response ,Scrapy 不会调用 其他的process_request() or process_exception() 以及下载方法.直接返回 这个Response. 但是 process_response() 什么时候都会被调用
* 返回 Request ,Scrapy 将会停止调用 process_request 方法 并重新安排这个返回的请求.(这个方法未知)
* 返回 IgnoreRequest, process_exception() 方法会被调用.如果没有被调用,err back方法被调用. 仍然没有,就会被忽略
process_response(request, response, spider)
返回一个Response 对象,或一个Request 对象,或者抛出一个IgnoreRequest 异常.
返回 Response: 将会被下一个中间件链中的 process_response() 处理.
返回 Request : 中间件链将会终止,这个请求会被重新安排到下载中心.这个行为类似process_request()的返回Request.
返回 IgnoreRequest: Request.errback 函数会被调用,如果没有处理这个异常,这个异常将会被忽略并且不会有log输出(不同于其他的异常).
激活请求中间件
方法和下载中间件类似,在配置文件中注册
SPIDER_MIDDLEWARES = { 'myproject.middlewares.CustomSpiderMiddleware': 543,
}
process_spider_input(response, spider)
每一个请求响应在返回Spider被处理之前调用这个方法. 方法返回 None 或抛出一个异常.
返回 None : Scrapy 继续执行余下流程
抛出一个异常: Scrapy 将终止调用其他的 spider 中间件的 process_spider_input
方法,并调用 request errback函数. 这个err back 函数返回的结果会被
process_spider_output() 函数处理,如果err back也抛出一个异常.
,则process_spider_exception()函数会被调用.
process_spider_output(response, result, spider)
这个方法会在response被spider处理后调用.
process_spider_output() 必须返回一个Request, dict or Item objects
process_spider_exception(response, exception, spider)
当 一个spider 或者process_spider_input()(来自其他spider的中间件) 方法抛出一个异常
process_spider_exception() 应该返回一个 None 或一个可迭代的 Response, dict 或Item objects.
返回 None :Scrapy 继续处理这个异常.执行余下流程中的其他的中间件process_spider_exception() 函数,直到没有中间件.最终这个异常会到达engine(将会有log)
返回一个可迭代的对象: 异常链终止
Spider Middleware 内建中间件
{
'scrapy.spidermiddlewares.httperror.HttpErrorMiddleware': 50,
'scrapy.spidermiddlewares.offsite.OffsiteMiddleware': 500,
'scrapy.spidermiddlewares.referer.RefererMiddleware': 700,
'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware': 800,
'scrapy.spidermiddlewares.depth.DepthMiddleware': 900,
}