Scrapy爬取豆瓣9分榜单
2017-06-07 17:55
197 查看
Scrapy简介
Python开发的一个快速,高层次的屏幕抓取和web抓取框架,用于抓取web站点并从页面中提取结构化的数据。Scrapy用途广泛,可以用于数据挖掘、监测和自动化测试。Scrapy吸引人的地方在于它是一个框架,任何人都可以根据需求方便的修改。
环境配置
我使用的是Anaconda+PyCharm,所以在Anaconda Prompt下运行:conda install scrapy
安装完成后,直接就可以使用scrapy命令来创建爬虫程序,命令行下切换到目标文件夹,输入以下命令:
我们cd进目录,然后按提示输入,这里我们爬虫取名为dbbook,网址就是https://www.douban.com/doulist/1264675/:
这样一个基本的爬虫框架就搭建好了。
如何提取
有了上面的框架抓取数据就很方便了,数据有了,下面如何提取我们想要的数据呢?1、正则表达式
正则表达式是一个大头!在html网页源代码中,我们需要找的内容一般都是被某些标签包围的,如果我们能保证找到我们需要的内容左右的标签(并且他们是独一无二的)那么我们很容易写出一个正则表达式,把其中的内容提取出来:<XXX>(.*?)</XXX>
( ) :表示这个内容是我们需要提取的
.* :表示匹配任意字符0到n次
?:表示非贪心,找对第一个就停下来
python正则模块使用
python的正则模块是re,主要用的函数是(re.S的意思是让”.”可以匹配换行符,不然有些标签头和尾是分几行的,就会匹配失败)
findall(pattern,str,re.S)
主力部队,把所有满足正则的内容提取出来,用于匹配满足某个条件的大量我们需要的内容。(比如所有的图片,所有的网址,所有的回复,所有的链接……)。它在网页提取中占了主要地位,工作量大,任务重,所以是主力部队。
search(pattern,str,re.S)
狙击手,用来匹配第一个找到的元素,它的目标目的就是找到我们明显知道只有一个的元素比如标题什么的,一旦找到就结束,所以它的执行速度很快。它的目标明确,效率高,所以是狙击手的角色。
sub(pattern,str,replace)
后勤,它的功能是替换,一般用于替换一个网页地址中的关键词,替换页码等。它看似不重要,但是往往能在很多方面给我们提供便利,所以是后勤。
注意:正则有时候一步不能完成我们需要的功能,可能需要进行几步操作,这时候,我们一般先提取大的部分,在从大部分里面提取我们需要的部分。
2、XPath
XPath即为XML路径语言,它是一种用来确定XML(标准通用标记语言的子集)文档中某部分位置的语言。XPath基于XML的树状结构,提供在数据结构树中找寻节点的能力。起初 XPath 的提出的初衷是将其作为一个通用的、介于XPointer与XSLT间的语法模型。但是 XPath 很快的被开发者采用来当作小型查询语言。下面这个例子生动的说明了XPath与正则表达式的不同:
如果你提取信息就像让你找一栋建筑,那么正则就是告诉你建筑左边是什么,右边是什么,但是全国可能有很多都满足条件的,你找起来还是不方便。
红房子(.*?)绿房子
XPath就是告诉你,这个建筑在北京市——海淀区——中关村——15号街——那栋黄色的建筑,你可以马上找到对应的建筑。如果一个地名只有一个地方有,那么你更可以简化成“中关村——15号街。
//北京市/海淀区/中关村/15号街[@房子颜色=黄色]/text() //中关村/15号[@房子颜色=黄色]/text()
也许在这里你还没能体会到他们之间的差别,但是相信我,当你遇到复杂的html分析的时候,你会发现它的厉害之处的。比如下面的例子,我想把Hello,my world!打印出来用正则需要考虑一下吧?但是用XPath就简单很多。
<div id="class">Hello, <font color=red>my world! <div>
XPath语法
XPath一般你需要知道这些语法,想了解更多请查阅相关资料。
// 根节点
/ 下一层路径
[@XX=xx] 特定的标签
/text() 以文本返回
/@para 返回参数 > string(.)
start-with(str) 所有以这个str开头的标签
下面是一个简单的例子
from lxml import etree html=''' <div class="test">content1</div> <div class="test">content2</div> <div class="test">content3</div> ''' selector = etree.HTML(html) content = selector.xpath(u"//div") for each in content: print(each.text) html1=''' <div id="class">Hello, <font color=red>my</font> <font color=green>world!</font> <div> ''' selector = etree.HTML(html) tmp = selector.xpath(u'//div[@class="test"]')[0] info = tmp.xpath('string(.)') content2 = info.replace('\n','') print(content2)
上边仅仅是几个简单的XPath例子,XPath实际上要比这远远强大的多。
3、Selectors选择器
从网页中提取数据有很多方法。Scrapy使用了一种基于 XPath 和 CSS 表达式机制: Scrapy Selectors。为了配合XPath,Scrapy除了提供了 Selector 之外,还提供了方法来避免每次从response中提取数据时生成selector的麻烦。Selector有四个基本的方法:
xpath(): 传入xpath表达式,返回该表达式所对应的所有节点的selector list列表 。
css(): 传入CSS表达式,返回该表达式所对应的所有节点的selector list列表.
extract(): 序列化该节点为unicode字符串并返回list。
re(): 根据传入的正则表达式对数据进行提取,返回unicode字符串list列表。
开始爬虫
1、运行起来
我们进入spiders/dbbook,查看start_urls的值是否有误(你需要提取目标网页的正确网址), 然后把allowed_domains注掉并且,把parse里面改成print response.body
命令行下输入scrapy crawl dbbook命令,执行一下。
运行爬虫也在此目录下随意建个文件,加上如下代码:
from scrapy import cmdline cmdline.execute("scrapy crawl dbbook".split())
这本质就是上面使用命令的方式。
运行发现没打印东西,看看,原来是403,爬虫被屏蔽了,有些网站可能做了一些简单的反爬虫,这里要加一个请求头部,模拟浏览器登录。
在settings.py里加入如下内容就可以模拟浏览器了
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.3; WOW64; rv:45.0) Gecko/20100101 Firefox/45.0'
我们再运行,发现网页内容已经被爬取下来了
2、开始编码
使用Google浏览器的开发者工具快速定位到我们需要的信息。根据先大后小的原则,我们先用bd doulist-subject,把每个书找到,然后,循环对里面的信息进行提取,我们看下每本书下面的内容:
提取书大框架:
'//div[@class="bd doulist-subject"]'
提取题目:
'div[@class="title"]/a/text()'
提取得分:
'div[@class="rating"]/span[@class="rating_nums"]/text()'
提取作者:(这里用正则方便点)
'<div class="abstract">(.*?)<br'
接下来我们在前面输出response的位置编写代码,那里就是我们要对数据处理的地方。
def parse(self, response): # print response.body selector = scrapy.Selector(response) books = selector.xpath('//div[@class="bd doulist-subject"]') for each in books: title = each.xpath('div[@class="title"]/a/text()').extract()[0] rate = each.xpath('div[@class="rating"]/span[@class="rating_nums"]/text()').extract() author = re.search('<div class="abstract">(.*?)<br', each.extract(), re.S).group(1) print "title:"+title print "rate:"+rate print author
运行我们的爬虫程序,会发现爬虫只进行一半就抛出异常了,在获取rate时出现了问题,我们找到最后能爬取得那本书的下本书,发现这个地方少了一个评分:
因此我们修改程序如下:
def parse(self, response): # print response.body item = DoubanbookItem() selector = scrapy.Selector(response) books = selector.xpath('//div[@class="bd doulist-subject"]') for each in books: title = each.xpath('div[@class="title"]/a/text()').extract()[0] rates = each.xpath('div[@class="rating"]/span[@class="rating_nums"]/text()').extract() if rates: rate = rates[0] author = re.search('<div class="abstract">(.*?)<br', each.extract(), re.S).group(1) title = title.replace(' ', '').replace('\n', '') author = author.replace(' ', '').replace('\n', '') print "title:"+title print "rate:"+rate print author
其中,replace是替换空号和换行符。好了,这时候再运行我们的程序,就可以提取完整的信息了。
3、爬取下页
下面我们再接再厉,爬取剩下页,依然通过开发者工具查看网页信息:因为只有这里会出现标签,所以用xpath轻松提取
'//span[@class="next"]/link/@href'
然后提取后 我们scrapy的爬虫怎么处理呢?答案是使用yield,yield可以让scrapy自动去处理。
yield scrapy.http.Request(url,callback=self.parse)
这样爬虫就会自动执行url的命令了,处理方式还是使用我们的parse函数
修改后的代码:
def parse(self, response): # print response.body item = DoubanbookItem() selector = scrapy.Selector(response) books = selector.xpath('//div[@class="bd doulist-subject"]') for each in books: title = each.xpath('div[@class="title"]/a/text()').extract()[0] rates = each.xpath('div[@class="rating"]/span[@class="rating_nums"]/text()').extract() if rates: rate = rates[0] author = re.search('<div class="abstract">(.*?)<br', each.extract(), re.S).group(1) title = title.replace(' ', '').replace('\n', '') author = author.replace(' ', '').replace('\n', '') print "title:"+title print "rate:"+rate print author
nextPage = selector.xpath('//span[@class="next"]/link/@href').extract()
if nextPage:
next = nextPage[0]
yield scrapy.http.Request(next, callback=self.parse)
好了,我们再运行一下,可以发现,运行的特别快,十几页几秒钟就运行完了。
保存提取信息
好了,剩下的事情就是如何把结果写入文件或数据库了,这里我采用写入文件。items.py
关于这个items.py,你只要考虑它就是一个存储数据的容器,可以考虑成一个结构体,你所有需要提取的信息都在这里面存着。
这里我们需要存储3个变量,title,rate,author,所以我在里面加入三个变量,就这么简单:
title = scrapy.Field() rate = scrapy.Field() author = scrapy.Field()
pipelines.py
一般来说,如果你要操作数据库什么的,需要在这里处理items,这里有个process_item的函数,你可以把items写入数据库,但是今天我们用不到数据库,scrapy自带了一个很好的功能就是Feed exports,它支持多种格式的自动输出。所以我们直接用这个就好了,pipelines维持不变。
settings.py
Feed 输出需要2个环境变量:
FEED_URI = u'file:///D://douban.csv' FEED_FORMAT = 'CSV'
FEED_FORMAT :指示输出格式,csv/xml/json/
FEED_URI : 指示输出位置,可以是本地,也可以是FTP服务器
FEED_URI改成自己的就行了
dbbook.py
修改后完整代码如下:
# -*- coding: utf-8 -*- import scrapy import re from doubanbook.items import DoubanbookItem class DbbookSpider(scrapy.Spider): name = "dbbook" #allowed_domains = ["www.douban.com/doulist/1264675"] start_urls = ['https://www.douban.com/doulist/1264675/'] def parse(self, response): # print response.body item = DoubanbookItem() selector = scrapy.Selector(response) books = selector.xpath('//div[@class="bd doulist-subject"]') for each in books: title = each.xpath('div[@class="title"]/a/text()').extract()[0] rates = each.xpath('div[@class="rating"]/span[@class="rating_nums"]/text()').extract() if rates: rate = rates[0] author = re.search('<div class="abstract">(.*?)<br', each.extract(), re.S).group(1) title = title.replace(' ', '').replace('\n', '') author = author.replace(' ', '').replace('\n', '') item["title"] = title item["rate"] = rate item["author"] = author yield item # print "title:"+title # print "rate:"+rate # print author nextPage = selector.xpath('//span[@class="next"]/link/@href').extract() if nextPage: next = nextPage[0] print next yield scrapy.http.Request(next, callback=self.parse)
好拉,运行一下,可以看见D盘出现了一个douban.csv的文件。
用Subline Text 打开看下:
这里只截取最后的部分结果,总共455条记录。
本篇博客大部分都参考这篇帖子,并通过自己实际操作总结而成,方便以后拾得快。
关于Scrapy比较全的一个教程。
相关文章推荐
- 【图文详解】scrapy安装与真的快速上手——爬取豆瓣9分榜单
- scrapy入门学习笔记之爬取豆瓣9分榜单
- 基于Python2.7和Scrapy, 爬取豆瓣9分榜单
- 【图文详解】scrapy安装与真的快速上手——爬取豆瓣9分榜单
- 使用scrapy简易爬取豆瓣9分榜单图书并存放在mysql数据库中
- 【图文详解】scrapy安装与真的快速上手——爬取豆瓣9分榜单
- 用Scrapy抓取豆瓣小组数据(一)
- 爬虫学习(四)mongoDB与Scrapy---优化豆瓣电影实例
- scrapy抓取豆瓣数据(2)
- Scrapy 通过登录的方式爬取豆瓣影评数据
- scrapy爬取豆瓣TOP250电影
- 用scrapy框架爬取豆瓣Top250电影
- 用Scrapy抓取豆瓣小组数据(二)
- Scrapy爬豆瓣电影Top250并存入MySQL数据库
- 使用爬虫爬取豆瓣2016电影榜单中所有电影
- python爬虫入门笔记:用scrapy爬豆瓣
- 第三百三十五节,web爬虫讲解2—Scrapy框架爬虫—豆瓣登录与利用打码接口实现自动识别验证码
- 用Scrapy抓取豆瓣小组数据(三)
- android 基于豆瓣 Api 的客户端,包含电影分类,图书分类,电影榜单,收藏功能,搜索功能。
- scrapy爬取豆瓣电影top250并存储到mysql