每一篇讲解scrapy框架的文章,都会以这张图来说明,对于一个小白,第一次看到这张图的时候,肯定是懵逼的!先不管这张图,下面我以抓取知乎的实例,说明这个框架!
通过以下命令,我们会建立一个scrapy框架的抓取知乎网页的工程:
scrapy startproject zhihu
这个时候就会生成以下这些文件!
这个时候我们需要进入这个工程zhihu,输入以下命令,建立spider文件!
scrapy genspider zhihuspider zhihu.com
这个时候就会生成以下这些文件!
这个时候,我们整个爬虫的框架就建立了!我们这个时候发现,这些文件的名字和上面框架图这个组件的名字好像蛮像的哦!!!
既然框架实现了,那我们就开始爬呗!本文要实现的是以轮子哥的主页为起点,爬取他关注的人和关注他的人的基本信息!
我们的zhihuspider.py这个文件实现的类型就是对应框架图的Spiders,它会向调度器Scheduler发起Requests请求,调度器会向Downloader发起请求,然后把返回Response返回给Spiders,中间还会通过Spider Middleware和Downloader Middleware这两玩意儿,我们先不管它们,先实现zhihuspider.py再说!
from scrapy import Request,Spider
import json
from zhihu.items import UserItem
class ZhihuspiderSpider(Spider):
name = 'zhihuspider'
allowed_domains = ['www.zhihu.com']
start_urls = ['https://www.zhihu.com/']
follow_url = 'https://www.zhihu.com/api/v4/members/{user}/followees?include={include}&offset={offset}&limit={limit}'
followers_url = 'https://www.zhihu.com/api/v4/members/{user}/followers?include={include}&offset={offset}&limit={limit}'
start_user = 'excited-vczh'
follows_query = 'data[*].answer_count,articles_count,gender,follower_count,is_followed,is_following,badge[?(type=best_answerer)].topics'
followers_query = 'data[*].answer_count,articles_count,gender,follower_count,is_followed,is_following,badge[?(type=best_answerer)].topics'
user_url = 'https://www.zhihu.com/api/v4/members/{user}?include={include}'
user_query = ' allow_message,is_followed,is_following,is_org,is_blocking,employments,answer_count,follower_count,articles_count,gender,badge[?(type=best_answerer)].topics'
def start_requests(self):
yield Request(self.user_url.format(user=self.start_user,include=self.user_query),callback=self.parse_user)
yield Request(self.follow_url.format(user=self.start_user,include=self.follows_query,offset=0,limit=20),
callback=self.parse_follows)
yield Request(self.followers_url.format(user=self.start_user, include=self.followers_query, offset=0, limit=20),
callback=self.parse_followers)
def parse_user(self,response):
result = json.loads(response.text)
item = UserItem()
for field in item.fields:
if field in result.keys():
item[field] = result.get(field)
yield item
yield Request(self.follow_url.format(user=result.get('url_token'),include=self.follows_query,limit=20,offset=0),
self.parse_follows)
yield Request(self.followers_url.format(user=result.get('url_token'), include=self.followers_query, limit=20, offset=0),
self.parse_followers)
def parse_follows(self, response):
results = json.loads(response.text)
if 'data' in results.keys():
for result in results.get('data'):
yield Request(self.user_url.format(user=result.get('url_token'),include=self.user_query),self.parse_user)
if 'paging' in results.keys() and results.get('paging').get('is_end') == False:
next_page = results.get('paging').get('next')
yield Request(next_page,self.parse_follows)
我们通过这个程序,可以看到它主要做两件事情,1、定义爬取网站的动作;2、分析爬取下来的网页。
以初始的url来初始化Request,并设置回调函数。当Request成功请求并返回时,Response生成并作为参数传回回调函数!我们看到的callback就是回调函数,当指定了该回调函数的请求完成后,获取到响应,引擎会将该响应作为参数传递给这个回调函数。回调函数进行解析或生成下一个请求,回调函数如上文的parse函数所示。
在代码中,有这样一句item = UserItem(),这个是用来实例化Item的,我们可以把它理解成一个字典,在声明的时候需要实例化,而它的实现是在items.py中。
from scrapy import Item,Field
class UserItem(Item):
# define the fields for your item here like:
# name = scrapy.Field()
name = Field()
answer_count = Field()
follower_count = Field()
headline = Field()
url = Field()
url_token = Field()
Item是保存爬取数据的容器,它的使用方法和字典类似!相比于字典,Item多了额外的保护机制,可以避免拼写错误或者定义字段错误!
在框架图中,Spider的数据items会到Item Pipelines中!上面我们说明了Item了,下面就说说Item Pipelines!注意,此处Item Pipelines中Item和上面那个item.py的item不是指的同一个东西啊!!!
Item Pipelines主要做以下这些事情,1、清理HTML数据;2、验证爬取数据,检查爬取字段;3、查重并丢弃重复内容;4、将爬取结果保存到数据库。
我们先看看pipelines.py的实现!
import pymongo
class MongoPipeline(object):
collection_name = 'user'
def __init__(self, mongo_uri, mongo_db):
self.mongo_uri = mongo_uri
self.mongo_db = mongo_db
@classmethod
def from_crawler(cls, crawler):
return cls(
mongo_uri=crawler.settings.get('MONGO_URI'),
mongo_db=crawler.settings.get('MONGO_DB','items')
)
def open_spider(self,spider):
self.client = pymongo.MongoClient(self.mongo_uri)
self.db = self.client[self.mongo_db]
def close_spider(self, spider):
self.client.close()
def process_item(self, item, spider):
#self.db[self.collection_name].insert_one(dict(item))
self.db[self.collection_name].update({'url_token':item['url_token']},{'$set':dict(item)},True)
return item
这个程序实现的方法中,process_item()是必须实现的方法,被定义的Item Pipeline会默认调用这个方法对Item进行处理。比如这里就是将数据写入数据库!这里我们看到有两个参数,其中item就是每次spider生成的Item都会作为参数传递过来,而spider就是spider的实例!
from_crawler()方法是一个类方法,用@classmethod标识,是一种依赖注入的方式。它的参数是crawler,通过crawler对象,我们会拿到Scrapy的所有核心组件,如全局配置的每个信息,然后创建一个pipeline的实例!参数cls就是class,最后返回一个class实例!
这样,我们就把框架图的组件都有了说明,还剩下Spider Middleware和Downloader Middleware。这两个东西的实现在middleware.py这个文件中!
其实这两个都是中间的处理模块,其中Downloader Middleware即下载中间件,处于Request和Response之间的处理模块!可以用来修改User-Agent、处理重定向、设置代理、失败重试、设置Cookie等功能!
而Spider Middleware主要用来处理Request、Response、Item,是介于Scrapy和Spider处理机制的钩子框架!
现在把框架上面的东西都讲完了,还有一个setting.py这个文件,顾名思义,就是做一些配置而已,比如我们用到的MongoDB就是在里面配置的!
ITEM_PIPELINES = {
'zhihu.pipelines.MongoPipeline': 300,
}
MONGO_URI = 'localhost:27017'
MONGO_DB = 'zhihu'
那么,整个框架的代码就讲完了,运行它,抓取到下面这样的数据了!
参考《Python3网络爬虫实战》