结合上一篇博文《Scrapy爬虫—-(一)命令行工具》中讲解的一些常用的命令我们可以很方便的创建一个Scrapy项目,这篇文章便可以开始我们的第一个Scrapy爬虫项目:爬取一个简单的静态网页中的小说内容:http://yetianlian.com
下图便是我们利用上一篇文章中的两个命令创建的scrpay项目目录结构以及文件介绍 :
这里项目是从开始爬取一个简单的静态网页入手:http://www.yetianlian.com/ (这个静态网页涉及到的反爬虫策略较少,方便新手学习)项目的目的是获取下图中文章中的标题,章节标题,每一小节的标题以及每一小节的小说内容最终以“小节名.txt”文件格式存入按以上标题级别创建的文件目录下。
想实现最终的目标,我们第一步要做的是确定我们要爬取的内容是那些,定义在items.py文件中,然后在ytl.py文件中编写parse方法, 利用pipelines.py文件对数据进行处理,最后修改settings.py文件,其中每一个步骤都不能遗漏,最后运行crawl spider命令便可以按照我们所需要的内容对网页解析源码,抓取数据,存储数据。
首先进入spiders文件夹下查看ytl.py自动生成的spider代码
爬虫模块的代码都放置在这个文件夹中,爬虫模块是用于从单个或者多个网站爬取数据的类。其中应该包括有初始页面的URL,跟进的网页链接,分析页面内容,提取需要的数据函数。创建一个Spider类需要继承scrapy.Spider类,其中定义有一下三个属性:name,allowed_domains,start_urls,具体属性的介绍在下面的代码的注释部分体现:
ytl.py源代码:
class YtlSpider(scrapy.Spider):
# 爬虫的唯一名字,不能为不同的spider设置相同的名字
name = 'ytl'
# 爬虫允许爬取URL的域名范围,这个爬虫允许的爬取的范围为yetianlian.com
allowed_domains = ['yetianlian.com']
# start_urls 是spider在启动时进行爬取的入口的URL列表。因此,第一个被获取的URL也是其中之一
# 后续的URL则会从初始的URL的响应中主动提取
start_urls = ['http://yetianlian.com']
# parse() 函数,是spider的一个方法。被调用时,根据初始的URL响应后的返回的Response对象
# 将会作为唯一的参数传递给该方法。该方法有三个功能
# (1.解析返回的数据(response data) 2.提取数据(生成item) 3.生成需要进一步处理的URL的Request对象)
def parse(self, response):
# pass是空语句,是为了保持程序结构的完整性。pass 不做任何事情,一般用做占位语句
pass
我们根据所要获取内容,可以再item中创建这些fields:chapternum、chaptername、chapterurl、chaptercontent。
Items.py源代码:
class Testdemo001Item(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
# 跟进的文章链接
url = scrapy.Field()
# 内部第几章
chapternum = scrapy.Field()
# 该章节的名称
chaptertitle = scrapy.Field()
# 该章节的url
chapterurl = scrapy.Field()
# 该章节的内容
chaptercontent = scrapy.Field()
pass
Item知识:
Item的目的是提供了一个方便让爬取的数据从非结构性的数据源提取结构性数据的简单容器,可以用来存储爬取到的数据,提供了类似于字典API以及用于声明可用字段的简单语法。我们定义的Item类需要继承scrapy.Item类,具体的代码如上。
Item的操作方式和字典很类似,这里讲常用的几个操作简单介绍:
创建对象:item = Testdemo001Item(title = ‘XXXX’, content = ‘XXX’)
获取字段的值:a = item[‘title’]
设置字段的值:item[‘title’] = ‘XXX’
获取所有键,值:item.keys(),item.values()
获取item中所有的key以及key对应的值:item.items()
dict与item转化:dict_item = dict(item)
Testdemo001Item可以被继承,可以添加更多的字段
默认生成的spider文件中没有提供特殊的功能,仅仅提供了start_request()的默认实现,可以读取并请求spider属性中的start_urls,并根据返回的response调用spider的parse方法。上面完成了网页下载的功能,接下来需要做的工作即是提取网页中的数据:从网页中提取我们需要的chapternum、chaptername、chapterurl、chaptercontent,然后将这些内容封装成Item对象以便于后续的存储操作。
在1板块中的ytl.py文件创建后,我们运行crawl spidername爬虫命令可以实现对网页下载的功能,仅有网页下载的功能我们无法获取所需要的数据,接下来是对网页数据的提取。
Scrapy有着自己的一套对于网页数据提取的机制,称为选择器selector(点击可以查看官方介绍),可以用selector通过特定的XPath和CSS表达式来选择HTML文件中的某个部分。scrapy选择器是建立在lxml解析库上,这意味scrapy在速度和解析准确度上比较的优秀。当然,我们也可以使用BeautifulSoup包进行解析,后面的文章会详细介绍Phantomjs+BeautifulSoup创建的爬虫。
Selector对象有四个基本方法:
xpath(query):传入XPath表达式query,返回该表达式所对应的所有节点的selector list 列表
css(query):传入CSS表达式query,返回该表达式所对应的所有节点的selector list 列表
extract()[]:序列化该节点为Unicode字符串并返回list列表,[n]可以精确定位返回的第n个字符串(从0开始索引,第0个字符串……)
re(regex):根据传入的正则表达式对数据进行提取,返回Unicode字符串列表。这里regex表示的是根据语法写的正则表达式。另外也可以使用re.compile(regex),官方给出具体的解释如下:regex can be either a compiled regular expression or a string which will be compiled to a regular expression using re.compile(regex) 【re模块中包含一个重要函数是compile(pattern [, flags]) ,该函数根据包含的正则表达式的字符串创建模式对象。可以实现更有效率的匹配。在直接使用字符串表示的正则表达式进行search,match和findall操作时,python会将字符串转换为正则表达式对象。而使用compile完成一次转换之后,在每次使用模式的时候就不用重复转换。当然,使用re.compile()函数进行转换后,re.search(pattern, string)的调用方式就转换为 pattern.search(string)的调用方式。】
而在YtlSpider类的parse()方法中,其中一个参数是response,将response传入selector中,便可以直接构造出一个Selector对象,进而可以调用以上的四个方法。写法可以使response.selector.xpath(query)一般使用简写的方式response.xpath(query),或者response.xpath(query)的形式。我们可以使用scarpy shell url来检测我们所获取的xpath或者css等是否正确,这里在上一篇文章中有介绍,这里不再赘述。如果对于xpath和regex(正则表达式)不是很清楚,后面的博客中会介绍关于xpath和regex部分。
网页分析:
这里使用的是GoogleChrome浏览器对网页进行F12查看元素(FireFox最新的版本已经关闭firebug和firepath等插件的使用,大部分功能均集中到Firefox Developer Edition版本中,考虑到版本不稳定,文章中采用的均是Chrome查看网页源码)
可以使用ctl+shift+c快捷键开始审查元素的功能,这里可以看到我们需要的内容所在网页对应的源码部分,选中该元素右键可以copy该元素的xpath(若是对于一个元素的获取,我们可以直接使用这个xpath,若是同类的元素,我们需要稍微修改一下xpath即可)
点击每一章节打开后的网页便是每一个章节小说的具体内容,我们可以使用同样的方法,获取小说内容在网页中所对应的源码部分
数据提取:
我们通过selector选择器和xpath来获取我们需要的内容,可以再ipython中查看我们所用的xpath是否正确
标题元素获取
图中第一个使用firefox自带的开发者工具审查元素获取的xpath,可以看出,是一个完整的路径,对于我们要获取相同元素的xpath修改不方便,第二个是chrome审查元素获取的xpath,比较符合xpath的书写,也便于我们后面的修改,一般都以后者书写。
副标题的xpath,我们可以根据//[@id=”post-1135”]/div/h3/text()进行修改为//[@id=”post-1135”]/div/h2/text()或者//*[@id=”post-1135”]//h2/text(),然后可以在scrapy shell下检测是否正确
可以看出子标题一共有三个,我们选择出来四个,说明多了一个,查看源码可以看出来,上面有一个标题和下面三个子标题xpath路径规则一样
我们可以采用extract()[]精确的获取我们需要的部分,其中的操作和python中list的用法一样,从‘0’开始索引,‘-1’表示结尾,[0:n]表示list中的第0个元素到第n-1个元素(不包括第n个元素),[-1]表示从list结尾开始数,倒数第一个元素,[-n]表示倒数第n个元素:
另外的几个部分也可按照同样的思路进行对网页的数据进行提取
在完成以上的任务之后,我们开始可以着手parse()方法,方法的目的:从网页中提取出我们需要的数据内容,然后封装成Item对象,方便下一步的进行
文章的内容均是博主码字上去的,在阅读的过程中如果遇到有问题请指出,若对文章中的内容如有不同的见解,欢迎一起学习讨论。