俏咪咪的给自己放了个小长假之后,我白汉三又回来啦。这次要开始慢慢系统学习scrapy了,这个框架真的很厉害,我只是写了个很小的例子,就只花了十行代码就完成了我之前的一篇文章的工作量,而且速度超快,个人认为非常值得学习,它上手简单并且通俗易懂
欢迎关注公众号:老白和他的爬虫
1.创建一个爬虫
在Anaconda+pycharm配置和Scrapy ——环境搭配与一个简单的例子这两篇文章里已经讲述了如何使用anaconda以及配置anaconda+pycharm环境,没有配置的老铁可以出门右拐,去那里配置一下,anaconda是我一直推荐使用的集成环境,它不需要你再pip很多的包了,直接通过可视化的窗口就可以安装第三方包。通过anaconda自带的spyder编辑器也可以使用scrapy但是需要配合windows下的命令提示符和mac下的终端来操作,相比之下还是pycharm方便一点。
在控制台输入创建一个项目tutorial
scrapy startproject tutorial
运行成功之后你可以看到你的pycharm的项目列表下有这样的目录
tutorial/
scrapy.cfg # deploy configuration file
tutorial/ # project's Python module, you'll import your code from here
__init__.py
items.py # project items definition file
middlewares.py # project middlewares file
pipelines.py # project pipelines file
settings.py # project settings file
spiders/ # a directory where you'll later put your spiders
__init__.py
在tutorial/spiders
目录下创建一个文件,命名为quotes_spider.py
,代码如下
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
def start_requests(self):
urls = [
'http://quotes.toscrape.com/page/1/',
'http://quotes.toscrape.com/page/2/',
]
for url in urls:
yield scrapy.Request(url=url, callback=self.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)
简单解释一下这个实例
- scrapy.Spider表示这个继承了这个子类,我们可以使用它的一些方法
- name:表示这个爬虫的名字,必须是独一无二的
- start_requests:返回可重复的Requests.通过自定义输入的起始地址,重复调用parse()解析网页
- parse():定义处理网页的方法
2.运行一个爬虫
对于上述已经创建好的爬虫,在控制台输入以下代码:
scrapy crawl quotes
到这里你要注意这个命令的格式scrapy
是我们使用scrapy框架必须要声明的,前面试过startproject
表示创建一个项目,crawl
表示运行一个爬虫,后面会试到更多的命令,这里的quotes
是我们刚在程序里声明的name属性,记住不是它的类的名称也不是文件名。
对于上面我们创建的代码,我们也可以这样简化一下
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
start_urls = [
'http://quotes.toscrape.com/page/1/',
'http://quotes.toscrape.com/page/2/',
]
def parse(self, response):
page = response.url.split("/")[-2]
filename = 'quotes-%s.html' % page
with open(filename, 'wb') as f:
f.write(response.body)
这段代码和上面相比,去掉了start_requests方法,只使用start_urls存放其实地址。这些初始地址将会被scrapy的默认方法start_requests()
解析,你仔细看代码会惊讶的发现根本就没有调用start_urls这个变量,这是因为parse是scrapy默认的方法。没错,就是这么神奇。
3.学会使用shell
官方的文档告诉我们通过shell命令来学会如何一步一步解析文档,但其实当我们在实际写爬虫的时候,可以利用这个命令来一步一步来修改代码,因为我们需要自己编写爬取信息的具体代码,谁也不敢保证你写的爬取信息的代码一次就成功,如果每一次都通过scrapy crawl * 这样的命令来调试,就太麻烦了,所以shell也是一个很好的调试工具。
输入命令
scrapy shell "http://quotes.toscrape.com/page/1/"
在这之前你可以访问一下网站http://quotes.toscrape.com/page/1/这是scrapy提供的事例网站。运行之后你应该能看到
这里就可以在shell下操作了,使用css选择器来选择相应标签
3.1使用css选择器
获取标签
>>> response.css('title')
[<Selector xpath='descendant-or-self::title' data='<title>Quotes to Scrape</title>'>]
获取标签下文本
>>> response.css('title::text').extract()
['Quotes to Scrape']
获取标签和文本
>>> response.css('title').extract()
['<title>Quotes to Scrape</title>']
上述获取标签所返回的结果都是列表形式的,下面两种方式选择第一个元素,第一种方式能够避免未找到标签而报错
>>> response.css('title::text').extract_first()
'Quotes to Scrape'
>>> response.css('title::text')[0].extract()
'Quotes to Scrape'
3.2使用xpath
除开css选择器,也可以通过xpath来选择标签。这里有必要解释一下xpath,XPath即为XML路径语言(XML Path Language),它是一种用来确定XML文档中某部分位置的语言,网页HTML也是一种XML。
>>> response.xpath('//title')
[<Selector xpath='//title' data='<title>Quotes to Scrape</title>'>]
>>> response.xpath('//title/text()').extract_first()
'Quotes to Scrape'
xpath非常的强大,scrapy selector就是基于xpath选择器完成的。
3.3实例
选取实例网站http://quotes.toscrape.com,它的html代码大致如下:
<div class="quote">
<span class="text">“The world as we have created it is a process of our
thinking. It cannot be changed without changing our thinking.”</span>
<span>
by <small class="author">Albert Einstein</small>
<a href="/author/Albert-Einstein">(about)</a>
</span>
<div class="tags">
Tags:
<a class="tag" href="/tag/change/page/1/">change</a>
<a class="tag" href="/tag/deep-thoughts/page/1/">deep-thoughts</a>
<a class="tag" href="/tag/thinking/page/1/">thinking</a>
<a class="tag" href="/tag/world/page/1/">world</a>
</div>
</div>
在pycharm里输入命令行代码
scrapy shell 'http://quotes.toscrape.com'
运行完成后,
>>> response.css("div.quote")
通过结果可以看到,返回了一个符合条件的列表,选择第一个作为我们想要的数据
>>> quote = response.css("div.quote")[0]
所获取的quote也就代表着实例网站中的一个节点
下面这段代码分别获取其中的正文、作者和标签
>>> title = quote.css("span.text::text").extract_first()
>>> title
'“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”'
>>> author = quote.css("small.author::text").extract_first()
>>> author
'Albert Einstein'
>>> tags = quote.css("div.tags a.tag::text").extract()
>>> tags
['change', 'deep-thoughts', 'thinking', 'world']
通过上面这段代码可以了解如何从一个节点中抽取信息,像实例网站中有那么多的节点,只需要设置一个循环就可以全部获取了
>>> for quote in response.css("div.quote"):
... text = quote.css("span.text::text").extract_first()
... author = quote.css("small.author::text").extract_first()
... tags = quote.css("div.tags a.tag::text").extract()
... print(dict(text=text, author=author, tags=tags))
{'tags': ['change', 'deep-thoughts', 'thinking', 'world'], 'author': 'Albert Einstein', 'text': '“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”'}
{'tags': ['abilities', 'choices'], 'author': 'J.K. Rowling', 'text': '“It is our choices, Harry, that show what we truly are, far more than our abilities.”'}
... a few more of these, omitted for brevity
>>>
4.在scrapy中爬取数据
在第三节里面教会你如何使用shell调试好爬虫代码,现在回到scrapy中去尝试自己的爬虫。到目前为止,除了保存整个HTML文件还没有正式的爬取数据
一个scrapy爬虫一般都是把数据保存在字典中,这里使用yield来保存数据
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
start_urls = [
'http://quotes.toscrape.com/page/1/',
'http://quotes.toscrape.com/page/2/',
]
def parse(self, response):
for quote in response.css('div.quote'):
yield {
'text': quote.css('span.text::text').extract_first(),
'author': quote.css('small.author::text').extract_first(),
'tags': quote.css('div.tags a.tag::text').extract(),
}
运行代码
scrapy crawl quotes
就可以看到结果了
5.保存你爬取的数据
爬取好的数据可以通过以下的指令来保存
scrapy crawl quotes -o quotes.json
可以看到这比我们之前的代码只多了-o quotes.json
,这个运行的结果都保存在了一个名为quotes.json
的json文件中。但是这个命令面临一个问题,如果你运行两次就会有问题,为了避免这个问题,可以改用以下代码
scrapy crawl quotes -o quotes.jl
命令效果完全相同,不同的是它可以多次运行,每次把新的结果接着之前的文件插入其后。
6.获取翻页链接
在之前的爬虫实战文章里,很重要的一个操作就是如何获取网站的翻页链接,在scrapy中也是一样,在前面的实例代码中,只给了两个链接。通过检查实例网站的翻页按钮,我们可以看到它的代码
<ul class="pager">
<li class="next">
<a href="/page/2/">Next <span aria-hidden="true">→</span></a>
</li>
</ul>
在shell里这样调试,首先获取a标签
>>> response.css('li.next a').extract_first()
'<a href="/page/2/">Next <span aria-hidden="true">→</span></a>'
然后再通过属性href
来获取地址
>>> response.css('li.next a::attr(href)').extract_first()
'/page/2/'
下面通过这段代码来实现获取所有翻页链接以及所有网页的信息
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
start_urls = [
'http://quotes.toscrape.com/page/1/',
]
def parse(self, response):
for quote in response.css('div.quote'):
yield {
'text': quote.css('span.text::text').extract_first(),
'author': quote.css('small.author::text').extract_first(),
'tags': quote.css('div.tags a.tag::text').extract(),
}
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)
这段代码仔细阅读一下也很好理解,只给定一个初始网页,再获取第一页数据之后,获取翻页链接,此时翻页链接的地址为相对地址,就像'/page/2/'
这样,我们在通过urljoin()方法,使它成为一个绝对地址,然后再利用scrapy对绝对地址发送一个新的request请求。
还有一个捷径直接通过相对地址也可以直接发送request请求,就像这样
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
start_urls = [
'http://quotes.toscrape.com/page/1/',
]
def parse(self, response):
for quote in response.css('div.quote'):
yield {
'text': quote.css('span.text::text').extract_first(),
'author': quote.css('span small::text').extract_first(),
'tags': quote.css('div.tags a.tag::text').extract(),
}
next_page = response.css('li.next a::attr(href)').extract_first()
if next_page is not None:
yield response.follow(next_page, callback=self.parse)
试一下同样也可以运行,甚至我们还可以简化
把后面获取翻页链接的代码替换为
for href in response.css('li.next a::attr(href)'):
yield response.follow(href, callback=self.parse)
又由于它本身就是a标签我们还可以利用它这个特性再继续简化,把上面的代码再改成
for a in response.css('li.next a'):
yield response.follow(a, callback=self.parse)
这样下来,你的代码就简介了很多,最后你也就通过了下面的代码就抓取了一个网站的所有信息。现在是不是一点点发现了scrapy的魅力了,之前我们抓取完全相同的网站华师信管官网,写了大概大几百行代码,现在我们利用了一下scrapy,十行代码搞定
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
start_urls = ['http://quotes.toscrape.com/page/1/',]
def parse(self, response):
for quote in response.css('div.quote'):
yield { 'text': quote.css('span.text::text').extract_first(),'author': quote.css('span small::text').extract_first(),'tags': quote.css('div.tags a.tag::text').extract(), }
for a in response.css('li.next a'):
yield response.follow(a, callback=self.parse)
不信你再回去瞅瞅我们第一篇纯手写的爬虫一个简单的爬虫——新闻爬虫
今天就到这了,关注一波吧
网友评论