全部源码解析文章索引目录传送门
【Scrapy 框架】版本 2.4.0 源码篇:全部配置目录索引
内容介绍
在抓取网页时,需要执行的最常见任务是从HTML源提取数据。
提取数据的方式有很多种根据自己的习惯就好。
BeautifulSoup是Python程序员中一个非常流行的Web抓取库,基于HTML代码的结构构造Python对象,并相当好地处理不好的标记,但它缺点是速度慢。
lxml是一个XML解析库(解析HTML),它使用基于ElementTree。(lxml不是Python标准库的一部分。)
Scrrapy有自己的提取数据的机制被称为Selectors,“选择”HTML文档的某些部分。
使用选择器
构建选择器
response 对象公开 Selector 实例上 .selector 属性
>>> response.selector.xpath('//span/text()').get()
'good'
使用XPath和CSS查询方式:response.xpath()和response.css()
>>> response.xpath('//span/text()').get()
'good'
>>> response.css('span::text').get()
'good'
文本构造方式
>>> from scrapy.selector import Selector
>>> body = '<html><body><span>good</span></body></html>'
>>> Selector(text=body).xpath('//span/text()').get()
'good'
构建response的HtmlResponse是TextResponse子类
>>> from scrapy.selector import Selector
>>> from scrapy.http import HtmlResponse
>>> response = HtmlResponse(url='http://example.com', body=body)
>>> Selector(response=response).xpath('//span/text()').get()
'good'
使用选择器
打开要解析的页面
构造一个XPath,用于选择title标记中的文本
>>> response.xpath('//title/text()')
[<Selector xpath='//title/text()' data='Example website'>]
提取文本数据调用选择器 .get() 或 .getall()
>>> response.xpath('//title/text()').getall()
['Example website']
>>> response.xpath('//title/text()').get()
'Example website'
>>> response.css('title::text').get()
'Example website'
.xpath()和.css()方法返回SelectorList实例,也可以快速嵌套数据
>>> response.css('img').xpath('@src').getall()
['image1_thumb.jpg',
'image2_thumb.jpg',
'image3_thumb.jpg',
'image4_thumb.jpg',
'image5_thumb.jpg']
提取第一个匹配的元素,使用 .get() 或者 .extract_first()
>>> response.xpath('//div[@id="images"]/a/text()').get()
'Name: My image 1 '
如果未抓取到对应数据元素
>>> response.xpath('//div[@id="not-exists"]/text()').get() is None
True
>>> response.xpath('//div[@id="not-exists"]/text()').get(default='not-found')
'not-found'
选择对应属性内容
>>> [img.attrib['src'] for img in response.css('img')]
['image1_thumb.jpg',
'image2_thumb.jpg',
'image3_thumb.jpg',
'image4_thumb.jpg',
'image5_thumb.jpg']
>>> response.css('img').attrib['src']
'image1_thumb.jpg'
对应选择的结果是唯一时
>>> response.css('base').attrib['href']
'http://example.com/'
构建图像url的几种方式
>>> response.xpath('//base/@href').get()
'http://example.com/'
>>> response.css('base::attr(href)').get()
'http://example.com/'
>>> response.css('base').attrib['href']
'http://example.com/'
>>> response.xpath('//a[contains(@href, "image")]/@href').getall()
['image1.html',
'image2.html',
'image3.html',
'image4.html',
'image5.html']
>>> response.css('a[href*=image]::attr(href)').getall()
['image1.html',
'image2.html',
'image3.html',
'image4.html',
'image5.html']
>>> response.xpath('//a[contains(@href, "image")]/img/@src').getall()
['image1_thumb.jpg',
'image2_thumb.jpg',
'image3_thumb.jpg',
'image4_thumb.jpg',
'image5_thumb.jpg']
>>> response.css('a[href*=image] img::attr(src)').getall()
['image1_thumb.jpg',
'image2_thumb.jpg',
'image3_thumb.jpg',
'image4_thumb.jpg',
'image5_thumb.jpg']
CSS选择器扩展
按照W3C标准,CSS选择器不支持选择文本节点或属性值。但在web抓取上下文中选择这些内容是非常重要的,因此scrapy(Parsel)实现非标准伪元素:
- 若要选择文本节点,使用 ::text
- 若要选择属性值,使用 ::attr(name) 属性的名称
title::text 选择后代的子文本节点
>>> response.css('title::text').get()
'Example website'
*::text 选择当前选择器上下文的所有子代文本节点
>>> response.css('#images *::text').getall()
['\n ',
'Name: My image 1 ',
'\n ',
'Name: My image 2 ',
'\n ',
'Name: My image 3 ',
'\n ',
'Name: My image 4 ',
'\n ',
'Name: My image 5 ',
'\n ']
foo::text 如果foo元素,但不包含文本(即文本为空)
>>> response.css('img::text').getall()
[]
>>> response.css('img::text').get()
>>> response.css('img::text').get(default='none')
'none'
a::attr(href) 选择href子代链接的属性
>>> response.css('a::attr(href)').getall()
['image1.html',
'image2.html',
'image3.html',
'image4.html',
'image5.html']
嵌套选择器
选择方法 (.xpath()或.css()) 返回相同类型的选择器列表
>>> links = response.xpath('//a[contains(@href, "image")]')
>>> links.getall()
['<a href="image1.html">Name: My image 1 <br><img src="image1_thumb.jpg"></a>',
'<a href="image2.html">Name: My image 2 <br><img src="image2_thumb.jpg"></a>',
'<a href="image3.html">Name: My image 3 <br><img src="image3_thumb.jpg"></a>',
'<a href="image4.html">Name: My image 4 <br><img src="image4_thumb.jpg"></a>',
'<a href="image5.html">Name: My image 5 <br><img src="image5_thumb.jpg"></a>']
>>> for index, link in enumerate(links):
... href_xpath = link.xpath('@href').get()
... img_xpath = link.xpath('img/@src').get()
... print(f'Link number {index} points to url {href_xpath!r} and image {img_xpath!r}')
Link number 0 points to url 'image1.html' and image 'image1_thumb.jpg'
Link number 1 points to url 'image2.html' and image 'image2_thumb.jpg'
Link number 2 points to url 'image3.html' and image 'image3_thumb.jpg'
Link number 3 points to url 'image4.html' and image 'image4_thumb.jpg'
Link number 4 points to url 'image5.html' and image 'image5_thumb.jpg'
选择元素属性
XPath语法
>>> response.xpath("//a/@href").getall()
['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']
CSS选择器的扩展 (::attr(…)) 语法
>>> response.css('a::attr(href)').getall()
['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']
.attrib 语法
>>> [a.attrib['href'] for a in response.css('a')]
['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']
# 字典方式
>>> response.css('base').attrib
{
'href': 'http://example.com/'}
>>> response.css('base').attrib['href']
'http://example.com/'
# 空结果
>>> response.css('foo').attrib
{
}
带有正则表达式的选择器
Selector也有一个.re()使用正则表达式提取数据的方法。但与使用.xpath()或.css()方法,.re()返回字符串列表。所以你不能构造嵌套的.re()。
>>> response.xpath('//a[contains(@href, "image")]/text()').re(r'Name:\s*(.*)')
['My image 1',
'My image 2',
'My image 3',
'My image 4',
'My image 5']
.get() .extract_first()) .re(),命名.re_first()。使用它仅提取第一个匹配字符串:
>>> response.xpath('//a[contains(@href, "image")]/text()').re_first(r'Name:\s*(.*)')
'My image 1'
extract() and extract_first()
.extract()和.extract_first() 对标 .get()和.getall()
SelectorList.get() 与 SelectorList.extract_first() 相同
>>> response.css('a::attr(href)').get()
'image1.html'
>>> response.css('a::attr(href)').extract_first()
'image1.html'
SelectorList.getall() 与 SelectorList.extract() 相同
>>> response.css('a::attr(href)').getall()
['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']
>>> response.css('a::attr(href)').extract()
['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']
Selector.get() 与 Selector.extract() 相同
>>> response.css('a::attr(href)')[0].get()
'image1.html'
>>> response.css('a::attr(href)')[0].extract()
'image1.html'
列表方式操作Selector.getall()
>>> response.css('a::attr(href)')[0].getall()
['image1.html']
由于官网给出的实际内容在日常工作中很少用到,因此在专栏中会有一个实际应用检索列表供大家有选择性阅读。