Date: 2019-07-07
Author: Sun
1. 定义
Scrapy中的Selector类是基于lxml库建立的,并且简化了API接口。在使用的过程中先使用xpath或者css选择器选中页面中要提取的数据,然后进行提取。
从页面中提取数据的核心技术是HTTP文本解析,在python中常用的模块处理:
BeautifulSoup 非常流行的解析库,API简单,但解析的速度慢。
lxml 是一套使用c语言编写的xml解析库,解析速度快,API相对比较复杂。
2. 三者比较
-
BeautifulSoup缺点:慢
-
lxml:基于 ElementTree
-
Scrapy seletors: parsel library,构建于 lxml 库之上,这意味着它们在速度和解析准确性上非常相似
3. 提取过程
通过scrapy的下载器可以下载对应的网页内容,将内容封装到response对象中。
从页面中提取数据的核心技术是HTTP文本解析,在python中常用的模块处理:
BeautifulSoup 非常流行的解析库,API简单,但解析的速度慢。
lxml 是一套使用c语言编写的xml解析库,解析速度快,API相对比较复杂。
Scrapy中的Selector类是基于lxml库建立的,并且简化了API接口。在使用的过程中先使用xpath或者css选择器选中页面中要提取的数据,然后进行提取。
提取数据
调用Selector或者SelectList对象的以下几种方法可将选中的内容进行提取
(1)extract() 返回选中内容的Unicode字符串。
(2)extract_first() SelectorList专有,返回其中第一个Selector对象调用extract方法。通常SelectorList中只含有一个Selector对象的时候选择调用该方法。
(3)re() 使用正则表达式来提取选中内容中的某部分。
举个例子
selector.xpath('.//b/text()') .extract #[‘价格:99.00元’,‘价格:88.00元’,‘价格:88.00元’]
selector.xpath('.//b/text()').re('\d+.\d+') #[ '99.00','88.00','88.00']
(4) re_first() 返回SelectorList对象中的第一个Selector对象调用re方法。
selector.xpath('.//b/text()').re_first('\d+.\d+') # '99.00'
4 scrapy response自带xpath和css
构造选择器(selectors)
Scrapy selector是以 文字(text) 或 TextResponse
构造的 Selector
实例。 其根据输入的类型自动选择最优的分析方法(XML vs HTML):
实际的开发过程中,几乎不需要手动创建Selector对象,,Response对象以自身参数自动创建Selector对象
response.xpath('.//h1/text').extract() # [ 'song','shi','chao']
response.css('li::text').extract() # [ 'song','shi','chao']
案例分析
>>> from scrapy.selector import Selector
>>> from scrapy.http import HtmlResponse
以文字构造:
>>> body = '<html><body><span>good</span></body></html>'
>>> Selector(text=body).xpath('//span/text()').extract()
[u'good']
以response构造:
>>> response = HtmlResponse(url='http://example.com', body=body)
>>> Selector(response=response).xpath('//span/text()').extract()
[u'good']
为了方便起见,response对象以 .selector 属性提供了一个selector, 您可以随时使用该快捷方法:
>>> response.selector.xpath('//span/text()').extract()
[u'good']
4.1 Xpath选择器
xpath即xml路径语言,用来确定xml文档中某个部分的语言,xml文档(html属于xml)是一系列节点构成的树。
基础语法
**/ ** 选中文档的根 描述一个从根开始的绝对路径 ./表示从当前节点选取 (比如提取了一部分,还需要从提取出来里面进行提取就会使用,如果不加 **. **会从整个文档中开始提取)
. 选中当前节点 用来描述相对路径
**.. ** 选中当前节点的父节点 用来描述相对路径
ELEMENT 选中子节点中所有的ELEMENT元素节点
**//ELEMENT ** 选中子孙节点中所有的ELEMENT元素节点
*** ** 选中所有元素子节点
text() 选中所有文本子节点
@ATTR 选中名为ATTR的属性节点
@* 选中所有的属性节点
[谓语] 谓语用来查找某个特定的节点或者包含某个特定值的节点
举例
response.xpath('/html/body/div') #选取body下的所有div
response.xpath('//a') #选中文档所有a
response.xpath('/html/body//div') #选中body下的所有节点中的div,无论在什么位置
response.xpath('//a/text()') #选取所有a的文本
response.xpath('/html/div/*') #选取div的所有元素子节点
response.xpath('//div/*/img') #选取div孙节点的所有img
response.xpath('//img/@src') #选取所有img的src属性
response.xpath('//a[1]/img/@*') #选取第一个a下img的所有属性
response.xpath('//a[2]') #所有a中的第2个
response.xpath('//a[last()]') #所有a中的最后一个 ('/a[last()-1]')#倒第二个 ('//a[position()<=3]')#使用position函数选中前三个 ('//div[@id]')#选中所有含有id属性的div ('//div[@id="song"]')#选中所有id属性为song的div
response.xpath('//p[contains(@class,'song')]') #选择class属性中含有‘song’的p元素
response.xpath('//div/a | //div/p') 或者,页面中可能是a可能是p
4.2. CSS选择器
css即层叠样式表,选择器不如xpath强大,原理就是选择的时候翻译成xpath表达式在调用xpath方法。
*** ** 选择所有节点
/#container 选择id为container的节点
.container 选择class包含container的节点
li a 选择 所有 li 下的所有 a 节点ul + p ** 选择所有ul后面的第一个p元素
#container > ul 选择id为container的第一个ul节点
**a[class] ** 选取所有有class属性的a元素
a[href="http://b.com"] 含有href="http://b.com"的a元素
**a[href*='job'] ** 包含job的a元素
**a[href^='https'] ** 开头是https的a元素
**a[href$='cn'] **结尾是cn的a元素
response.css('div a::text').extract() 所有div下所有a的文本
response.css('div a::attr(href)').extract() href的值
response.css('div>a:nth-child(1)') 选中每个div的第一个a > 会设定只在子节点中找,不会到孙节点中
response.css('div:not(#container)') 选取所有id不是container的div
response.css('div:first-child>a:last-child') 第一个div中最后一个a
5. scrapy命令行工具分析网页元素
Scrapy是通过
scrapy
命令行工具进行控制的。 这里我们称之为 “Scrapy tool” 以用来和子命令进行区分。 对于子命令,我们称为 “command” 或者 “Scrapy commands”。
Scrapy tool 针对不同的目的提供了多个命令,每个命令支持不同的参数和选项。
使用 scrapy工具
您可以以无参数的方式启动Scrapy工具。该命令将会给出一些使用帮助以及可用的命令:
Scrapy X.Y - no active project
Usage:
scrapy <command> [options] [args]
Available commands:
crawl Run a spider
fetch Fetch a URL using the Scrapy downloader
[...]
您也可以查看所有可用的命令:
scrapy -h
Scrapy提供了两种类型的命令。一种必须在Scrapy项目中运行(针对项目(Project-specific)的命令),另外一种则不需要(全局命令)。全局命令在项目中运行时的表现可能会与在非项目中运行有些许差别(因为可能会使用项目的设定)。
全局命令:
- startproject
- settings
- runspider
- shell
- fetch
- view
- version
项目(Project-only)命令:
- crawl
- check
- list
- edit
- parse
- genspider
- deploy
- bench
startproject
- 语法:
scrapy startproject <project_name>
- 是否需要项目: no
在 project_name
文件夹下创建一个名为 project_name
的Scrapy项目。
例子:
$ scrapy startproject myproject
genspider
- 语法:
scrapy genspider [-t template] <name> <domain>
- 是否需要项目: yes
在当前项目中创建spider。
这仅仅是创建spider的一种快捷方法。该方法可以使用提前定义好的模板来生成spider。您也可以自己创建spider的源码文件。
例子:
$ scrapy genspider -l
Available templates:
basic
crawl
csvfeed
xmlfeed
#scrapy提供了两种基本的爬虫模板,basic和crawl,可以选择一种作为爬虫模板,会自动在spiders目录下生成爬虫模板文件
$ scrapy genspider -d basic
import scrapy
class $classname(scrapy.Spider):
name = "$name"
allowed_domains = ["$domain"]
start_urls = (
'http://www.$domain/',
)
def parse(self, response):
pass
#创建基于基础模板basic的爬虫
$ scrapy genspider -t basic example example.com
(或者直接写成scrapy genspider example example.com)
Created spider 'example' using template 'basic' in module:
mybot.spiders.example
#创建基于crawl模板的爬虫
$ scrapy genspider -t crawl example example.com
shell
运行在终端的工具,用来调试scrapy。
- 语法:
scrapy shell [url]
- 是否需要项目: no
以给定的URL(如果给出)或者空(没有给出URL)启动Scrapy shell。 查看 Scrapy终端(Scrapy shell)获取更多信息。
例子:
$ scrapy shell http://www.example.com/some/page.html
[ ... scrapy shell starts ... ]
说明:
如果在使用scrapy shell的时候出现错误
ModuleNotFoundError: No module named 'win32api'
解决方案:
在虚拟化环境中安装pypiwin32
pip install pypiwin32
fetch
- 语法:
scrapy fetch <url>
- 是否需要项目: no
使用Scrapy下载器(downloader)下载给定的URL,并将获取到的内容送到标准输出。
该命令以spider下载页面的方式获取页面。例如,如果spider有 USER_AGENT
属性修改了 User Agent,该命令将会使用该属性。
因此,您可以使用该命令来查看spider如何获取某个特定页面。
该命令如果非项目中运行则会使用默认Scrapy downloader设定。
例子:
$ scrapy fetch --nolog http://www.example.com/some/page.html
[ ... html content here ... ]
$ scrapy fetch --nolog --headers http://www.example.com/
{'Accept-Ranges': ['bytes'],
'Age': ['1263 '],
'Connection': ['close '],
'Content-Length': ['596'],
'Content-Type': ['text/html; charset=UTF-8'],
'Date': ['Wed, 18 Aug 2010 23:59:46 GMT'],
'Etag': ['"573c1-254-48c9c87349680"'],
'Last-Modified': ['Fri, 30 Jul 2010 15:30:18 GMT'],
'Server': ['Apache/2.2.3 (CentOS)']}
parse
- 语法:
scrapy parse <url> [options]
- 是否需要项目: yes
获取给定的URL并使用相应的spider分析处理。如果您提供 --callback
选项,则使用spider的该方法处理,否则使用 parse
。
支持的选项:
--spider=SPIDER
: 跳过自动检测spider并强制使用特定的spider--a NAME=VALUE
: 设置spider的参数(可能被重复)--callback
or-c
: spider中用于解析返回(response)的回调函数--pipelines
: 在pipeline中处理item--rules
or-r
: 使用CrawlSpider
规则来发现用来解析返回(response)的回调函数--noitems
: 不显示爬取到的item--nolinks
: 不显示提取到的链接--nocolour
: 避免使用pygments对输出着色--depth
or-d
: 指定跟进链接请求的层次数(默认: 1)--verbose
or-v
: 显示每个请求的详细信息
例子:
$ scrapy parse http://www.example.com/ -c parse_item
[ ... scrapy log lines crawling example.com spider ... ]
>>> STATUS DEPTH LEVEL 1 <<<
# Scraped Items ------------------------------------------------------------
[{'name': u'Example item',
'category': u'Furniture',
'length': u'12 cm'}]
# Requests -----------------------------------------------------------------
[]
crawl
- 语法:
scrapy crawl <spider>
- 是否需要项目: yes
运行。使用spider进行爬取。
例子:
$ scrapy crawl myspider
[ ... myspider starts crawling ... ]
check
- 语法:
scrapy check [-l] <spider>
- 是否需要项目: yes
运行contract检查。
例子:
$ scrapy check -l
first_spider
* parse
* parse_item
second_spider
* parse
* parse_item
$ scrapy check
[FAILED] first_spider:parse_item
>>> 'RetailPricex' field is missing
[FAILED] first_spider:parse
>>> Returned 92 requests, expected 0..4
list
- 语法:
scrapy list
- 是否需要项目: yes
列出当前项目中所有可用的spider。每行输出一个spider。
使用例子:
$ scrapy list
spider1
spider2
view
- 语法:
scrapy view <url>
- 是否需要项目: no
在浏览器中打开给定的URL,并以Scrapy spider获取到的形式展现。 有些时候spider获取到的页面和普通用户看到的并不相同。 因此该命令可以用来检查spider所获取到的页面,并确认这是您所期望的。
例子:
$ scrapy view http://www.example.com/some/page.html
[ ... browser starts ... ]
settings
- 语法:
scrapy settings [options]
- 是否需要项目: no
获取Scrapy的设定
在项目中运行时,该命令将会输出项目的设定值,否则输出Scrapy默认设定。
例子:
$ scrapy settings --get BOT_NAME
scrapybot
$ scrapy settings --get DOWNLOAD_DELAY
0
runspider
- 语法:
scrapy runspider <spider_file.py>
- 是否需要项目: no
在未创建项目的情况下,运行一个编写在Python文件中的spider。
例子:
$ scrapy runspider myspider.py
[ ... spider starts crawling ... ]
version
- 语法:
scrapy version [-v]
- 是否需要项目: no
输出Scrapy版本。配合 -v
运行时,该命令同时输出Python, Twisted以及平台的信息,方便bug提交。
deploy
0.11 新版功能.
- 语法:
scrapy deploy [ <target:project> | -l <target> | -L ]
- 是否需要项目: yes
将项目部署到Scrapyd服务。(后续介绍)
案例分析
运行在终端的工具,用来调试scrapy
案例:爬取千峰官网上千峰教育网页内容
scrapy shell "http://www.mobiletrain.org/?f=1kcom"
会进入到scrapy终端
[s] view(response) View response in a browser
此时会有一个response对象
response对象:
属性:
text: 字符串格式的html
body:二进制格式的html
url: 当前请求的url
status: 状态码
方法:
xpath查找课程:
>>> response.xpath('//div[@class="index-video-box"]/ul/li/a/div/span/text()').extract()
['HTML基础视频教程', 'Linux云主机实战视频教程', 'Java编程基础讲解视频教程', '全栈测试全套技术视频教程', 'PHP千万级秒杀项视频教程']
通过css查找课程:
>>> response.css(".index-video-box .fl::text").extract()
['HTML基础视频教程', 'Linux云主机实战视频教程', 'Java编程基础讲解视频教程', '全栈测试全套技术视频教程', 'PHP千万级秒杀项视频教程']
那么如何在scrapy shell中给请求配置请求头呢?如下:
$ scrapy shell ( -s USER_AGENT='Mozilla/5.0' )
...
>>> from scrapy import Request
>>> req = Request('yoururl.com', headers={"header1":"value1"})
>>> fetch(req)