使用scrapy框架写爬虫时一般会在start_urls中指定我们需要爬虫去抓取的网页的url,但是如何让我们的爬虫像搜索引擎中使用的爬虫一样具备自动多网页爬取的功能呢?本文通过自动抓取个人csdn博客的所有文章标题、阅读人数、创建时间来进行一个简单的说明。文中使用了两种不同的方法来实现。
首先我们来分析cdsn中博客中文章的url,如图所示可以发现不同的文章页面的url只有url末尾对应的一串数字编号不同,而且在每篇文章的下面会有连接指向上一篇或者下一篇文章。因此我们可以提取这个链接实现网页的自动抓取。
在scrapy框架上编写爬虫整体流程和上一篇文章中的流程一样这里不再重复直接贴代码。
items.py
import scrapy
class CsdnItem(scrapy.Item):
# define the fields for your item here like:
name = scrapy.Field()
title=scrapy.Field()
time=scrapy.Field()
readtimes=scrapy.Field()
article_url=scrapy.Field()
pass
pipelines.py
import codecs
import json
from scrapy import signals
class CsdnPipeline(object):
def __init__(self):
self.file=codecs.open('data.json','w',encoding='utf-8')
def process_item(self, item, spider):
line=json.dumps(dict(item))+'\n'
self.file.write(line.decode("unicode_escape"))
return item
def spider_closed(self,spider):
self.file.close()
settings.py
# -*- coding: utf-8 -*-
# Scrapy settings for csdn project
#
# For simplicity, this file contains only the most important settings by
# default. All the other settings are documented here:
#
# http://doc.scrapy.org/en/latest/topics/settings.html
#
BOT_NAME = 'csdn'
SPIDER_MODULES = ['csdn.spiders']
NEWSPIDER_MODULE = 'csdn.spiders'
# Crawl responsibly by identifying yourself (and your website) on the user-agent
#USER_AGENT = 'csdn (+http://www.yourdomain.com)'
USER_AGENT = 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:37.0) Gecko/20100101 Firefox/37.0'
ITEM_PIPELINES = {
'csdn.pipelines.CsdnPipeline':300,
}
DOWNLOAD_DELAY = 2
COOKIES_ENABLED=False
然后是最关键的爬虫部分,第一种方法我们还是使用普通的Spider来完成,在start_urls中放置要抓取的第一篇文章的地址,在抓取完这篇文章的基本信息后放入定义好的item中,然后提取出下一篇文章的连接地址,结合python中yield,通过Request函数请求这个链接并在回调函数中处理请求返回页面的内容。具体代码如下:
spider1.py
#-*- coding:utf-8 -*-
from scrapy.spider import Spider
from scrapy.http import Request
from scrapy.selector import Selector
from csdn.items import CsdnItem
class csdnSpider(Spider):
name="csdn"
download_delay=1
allowed_domains=["blog.csdn.net"]
start_urls=[
"http://blog.csdn.net/u012286517/article/details/49556723"
]
def parse(self,response):
sel=Selector(response)
item=CsdnItem()
title=sel.xpath('//div[@id="article_details"]/div/h1/span/a/text()').extract()
article_url = str(response.url)
time=sel.xpath('//div[@id="article_details"]/div[2]/div/span[@class="link_postdate"]/text()').extract()
readtimes=sel.xpath('//div[@id="article_details"]/div[2]/div/span[@class="link_view"]/text()').extract()
item['title']=[n.encode('utf-8').replace("\r\n","").strip() for n in title]
item['time']=[n.encode('utf-8') for n in time]
item['readtimes']=[n.encode('utf-8') for n in readtimes]
yield item
#get next url
urls=sel.xpath('//li[@class="next_article"]/a/@href').extract()
for url in urls:
print url
url="http://blog.csdn.net"+url
print url
yield Request(url,callback=self.parse)
第二种方法我们用scrapy框架中提供的CrawlSpider,它是Spider的派生类,普通的spider只会爬取start_urls中定义的路径网页,但CrawlSpider定义了一些规则(rule)来提供跟进link的方便的机制。 也许该spider并不是完全适合您的特定网站或项目,但其对很多情况都使用。 因此您可以以其为起点,根据需求修改部分方法。当然您也可以实现自己的spider。
除了从Spider继承过来的(您必须提供的)属性外,其提供了一个新的属性:
rules:
一个包含一个(或多个) Rule 对象的集合(list)。 每个 Rule 对爬取网站的动作定义了特定表现。 Rule对象在下边会介绍。 如果多个rule匹配了相同的链接,则根据他们在本属性中被定义的顺序,第一个会被使用。
该spider也提供了一个可复写(overrideable)的方法:
parse_start_url(response)
当start_url的请求返回时,该方法被调用。 该方法分析最初的返回值并必须返回一个 Item 对象或者 一个 Request 对象或者 一个可迭代的包含二者对象。
具体实现代码如下:
#-*- coding:utf-8 -*-
from scrapy.contrib.spiders import CrawlSpider, Rule
from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
from scrapy.selector import Selector
from csdn.items import CsdnItem
class csdnSpider2(CrawlSpider):
name="csdn2"
download_delay=1
allowed_domains=["blog.csdn.net"]
start_urls=[
"http://blog.csdn.net/u012286517/article/details/49556723"
]
rules = [
Rule(SgmlLinkExtractor(allow=('/u012286517/article/details'),
restrict_xpaths=('//li[@class="next_article"]')),
callback='parse_item',
follow=True)
]
def parse_item(self, response):
sel=Selector(response)
item=CsdnItem()
title=sel.xpath('//div[@id="article_details"]/div/h1/span/a/text()').extract()
article_url = str(response.url)
time=sel.xpath('//div[@id="article_details"]/div[2]/div/span[@class="link_postdate"]/text()').extract()
readtimes=sel.xpath('//div[@id="article_details"]/div[2]/div/span[@class="link_view"]/text()').extract()
item['title']=[n.encode('utf-8').replace("\r\n","").strip() for n in title]
item['time']=[n.encode('utf-8') for n in time]
item['readtimes']=[n.encode('utf-8') for n in readtimes]
yield item
在rules中我们定义了如何从爬取到的页面提取链接,在这例子中即链接满足‘/u012286517/article/details’正则表达式。同时通过restrict_xpath来限定只从页面特定的部分来抽取接下来将要爬取的链接。
与运行代码我们得到如下结果,其中红线部分代表抓取的一篇文章:
我们再看一下爬取的信息部分:其中我们可以清楚的看到请求次数以及响应次数为20,与博客中的文章数目一致。