Scrapy⼊门:爬⾍类详解(Parse()函数、选择器、提取数据)
安装 & 创建项⽬
# 安装Scrapy
pip install scrapy
# 创建项⽬
scrapy startproject tutorial # tutorial为项⽬名
# 创建爬⾍
scrapy genspider <;爬⾍名> <domain>
得到的⽬录结构如下:
tutorial/
scrapy.cfg # 配置⽂件
tutorial/ # 项⽬的模块
__init__.py
items.py # 定义items
middlewares.py # 中间件
pipelines.py # pipelines
settings.py # 设置⽂件
spiders/ # 爬⾍
__init__.py
spider1.py
...
爬⾍类
爬⾍类必须继承 scrapy.Spider,爬⾍类中必要的属性和⽅法:
1. name = "quotes":爬⾍名,必须唯⼀,因为需要使⽤ scrapy crawl "爬⾍名" 命令⽤来开启指定的爬⾍。
2. start_requests():要求返回⼀个 requests 的列表或⽣成器,爬⾍将从 start_requests() 提供的 requests 中爬取,例如:
# start_requests()
def start_requests(self):
urls = [
'scrape/page/1/',
'scrape/page/2/',
]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse)
3. parse():⽤于处理每个 Request 返回的 Response 。parse() 通常⽤来将 Response 中爬取的数据提取为数据字典,或者过滤出 URL 然后继续发出Request 进⾏进⼀步的爬取。
# parse()
def parse(self, response):
page = response.url.split("/")[-2]
filename = 'quotes-%s.html' % page
with open(filename, 'wb') as f:
f.write(response.body)
self.log('Saved file %s' % filename)
4. start_urls 列表:可以在爬⾍类中定义⼀个名为 start_urls 的列表替代 start_requests() ⽅法。作⽤同样是为爬⾍提供初始的 Requests,但代码更加的简洁。运⾏爬⾍后,名为 parse() 的⽅法将会被⾃
动调⽤,⽤来处理 start_url 列表中的每⼀个 URL:
start_urls = [
'scrape/page/1/',
'scrape/page/2/',
]
5. 运⾏爬⾍:
$ scrapy crawl quotes
运⾏爬⾍时发⽣了什么:Scrapy 通过爬⾍类的 start_requests ⽅法返回对象。在接收到每个 response 响应时,它实例化对象并调⽤与 request 相关的回调⽅法( parse ⽅法),并将 Response 作为其参数传递。
parse() 函数
函数⽆疑是爬⾍类中最重要的函数,它包含了爬⾍解析响应的主要逻辑。
学习使⽤ Scrapy 选择器的最佳⽅法就是使⽤ Scrapy shell,输⼊这个命令之后将会进⼊⼀个交互式的命令⾏模式:
scrapy shell 'scrape/page/1/'
下⾯将通过交互式命令实践来学习 Response 选择器:
CSS 选择器
response.css 返回的是⼀个 SelectorList 对象,它是⼀个Selector 对象构成的列表,例如:
>>> response.css('title')
[<Selector xpath='descendant-or-self::title' data='<title>Quotes to Scrape</title>'>]
正则表达式提取中文⽤ getall() ⽅法获取所有符合条件的字符串列表,⽤ get() 获取⾸个匹配的字符串。::text ⽤于去除标签(<tag>)。
>>> response.css('title::text').getall()
['Quotes to Scrape']
>>> response.css('title::text').get()
'Quotes to Scrape'
>>> response.css('title::text')[0].get()
'Quotes to Scrape'
使⽤ re() 相当于在 getall() 的基础上⽤正则表达式对内容进⼀步筛选
>>> response.css('title::text').re(r'Q\w+')
['Quotes']
XPath 选择器
XPath 选择器相较于 CSS 选择器更加强⼤。实际上在 Scrapy 内部,CSS 选择器最终会被转换成 XPath 选择器。
>>> response.xpath('//title')
[<Selector xpath='//title' data='<title>Quotes to Scrape</title>'>]
>>> response.xpath('//title/text()').get()
'Quotes to Scrape'
⽣成数据字典
要将 Response 中爬取的数据⽣成为数据字典,使⽤字典⽣成器,例如:
def parse(self, response):
for quote in response.css('div.quote'): # quote是SelectorList对象
yield {
'text': quote.css('::text').get(),
'author': quote.css('small.author::text').get(),
'tags': quote.css('div.tags a.tag::text').getall(),
}
存储数据到⽂件
最简单的⽅法是⽤。使⽤ -o 参数指定⼀个 json ⽂件⽤于存储 parse() 函数 yield 出的内容。
$ scrapy crawl quotes -o quotes.json -s FEED_EXPORT_ENCODING=utf-8
# 若有中⽂务必加上 -s FEED_EXPORT_ENCODING=utf-8
使⽤格式存储。由于历史原因,Scrapy 只会追加⽽⾮覆盖原先的 Json ⽂件,会导致第⼆次写⼊后 Json 格式被破坏。⽽使⽤ JSON Lines 格式 ( .jl )可以避免这个问题
$ scrapy crawl quotes -o quotes.jl
要对数据进⾏更多的操作(例如验证爬到的数据,去重等等),可以在 pipelines.py 中写⼀个。当然,如果只需要存储爬取到的数据则不需要。
提取 URL 进⾏深层爬取
例如要提取出下⼀页的 URL 地址进⾏进⼀步的爬取:
<li class="next">
<a href="/page/2/">Next <span aria-hidden="true">→</span></a> <!-- &rarr;表⽰右箭头 -->
</li>
通过以下两种⽅式都可以提取出 <a> 标签中的 href 属性:
>>> response.css('li.next a::attr(href)').get()
'/page/2/'
>>> response.css('li.next a').attrib['href']
'/page/2'
当在 parse() 中 yield 出的是⼀个 Request 对象时,Scrapy 会⾃动安排发送这个 request,当请求完成后继续调⽤ callback 参数所指定的回调函数,如下所⽰:
def parse(self, response):
for quote in response.css('div.quote'): # quote是SelectorList对象
yield {
'text': quote.css('::text').get(),
'author': quote.css('small.author::text').get(),
'tags': quote.css('div.tags a.tag::text').getall(),
}
next_page = response.css('li.next a::attr(href)').get()
if next_page is not None:
next_page = response.urljoin(next_page) # urljoin()⽅法可以⾃动将相对路径转换为绝对路径
yield scrapy.Request(next_page, callback=self.parse) # yield scrapy.Request()
response.follow()
建议使⽤更⽅便的 response.follow() 替代 scrapy.Request(),因为它直接⽀持相对路径,上⽂中代码可以简化如下:
next_page = response.css('li.next a::attr(href)').get()
if next_page is not None:
yield response.follow(next_page, callback=self.parse) # next_page = '/page/2/'
response.follow() 还⽀持直接使⽤ Selector 对象作为参数,⽆需提取出 URL,于是上述代码得到进⼀步简化:
for href in response.css('li.next a::attr(href)'):
yield response.follow(href, callback=self.parse) # href = [<Selector xpath='' data=''>]
注意 SelectorList 对象不能直接作为参数,下⾯的⽤法是错误的:
yield response.follow(response.css('li.next a::sattr(href)'), callback=self.parse)
针对 <a> 标签的 css 选择器,response.follow() 会⾃动使⽤其 href 属性,于是上述代码终极简化版本如下所⽰:
# CSS选择器
for a in response.css('li.next a'):
yield response.follow(a, callback=self.parse)
但是注意 XPath 选择器不能这么简写:
# 不能简化成 //div[@class='p_name']/a
for a in response.xpath("//div[@class='p_name']/a/@href"):
yield response.follow(a, callback=self.parse)
默认情况下,Scrapy 会帮我们过滤掉重复访问的地址,可以通过 Setting 设置。
scrapy crawl 附带参数
使⽤ -a 选项来给爬⾍提供额外的参数,提供的参数会⾃动变成爬⾍类的属性(使⽤ self.tag 或 getattr(self, 'tag', None) 获取),如下例,使⽤ -a tag=humor 命令⾏参数,最终数据将保存到 quotes-humor.json ⽂件:
$ scrapy crawl quotes -o quotes-humor.json -a tag=humor
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
def start_requests(self):
url = 'scrape/'
tag = getattr(self, 'tag', None)
if tag is not None:
url = url + 'tag/' + tag
yield scrapy.Request(url, self.parse)
def parse(self, response):
for quote in response.css('div.quote'):
yield {
'text': quote.css('::text').get(),
'author': quote.css('small.author::text').get(), }
next_page = response.css('li.next a::attr(href)').get() if next_page is not None:
yield response.follow(next_page, self.parse)
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论