因为 Scrapy 作为爬虫的一个工具来说功能比较强大,这里会分几篇文章来说明。
创建项目
Scrapy 的项目构建是通过终端完成的,在要放置项目的目录下执行:
scrapy startproject projectname
此时就在终端当前的目录下创建了名为 projectname 的项目。
项目结构
项目构建后,会自动生成一系列文件,这些文件共同构成了该爬虫项目,文件主要有:
- items.py:用来存放爬虫获取的数据模型,也就是事先对数据结构进行定义
- middlewares.py:用来存放 middlewares 的文件,包括 Downloader middlewares 和 Spider middlewares
- pipelines.py:用来将 items.py 中的数据模型进行存储
- settings.py:爬虫的配置信息(delay,pipeline,headers等)
- scrapy.cfg:项目配置文件
- spider 文件夹:爬虫文件夹
创建爬虫
虽然可以手动在 spider 下建立自己的爬虫,但是 Scrapy 中也提供了命令能够自动地生成爬虫文件:
scrapy genspider spiderfilename domainname
- spiderfilename:说明了爬虫的文件名
- domainname:则表示该爬虫要爬取的域名,也就是说要爬取位于该域名下的资源
执行上述命令之后,会生成与名为 spiderfilename 的 py 文件,文件中主要包含:
import scrapy
class spiderfilename(scrapy.Spider):
name = 'spiderfilename'
allowed_domains = ['domainname']
start_urls = ['domainname']
def parse(self, response):
pass
上边的程序就定义了一个爬虫,Scrapy 中的爬虫使用类实现,该类继承自 scrapy.Spider,类中的数据成员和函数为:
- name:爬虫名
- allowed_domains:即创建爬虫的域名,该爬虫只会爬取该域名下的网页
- start_urls:爬虫开始的 url,其实不一定是 domainname,但一定是 domainname 下的网页
- parse:根据 Scrapy 的框架,Engine 会将 Downloader 返回的响应数据返回给 Spider 解析,而爬虫再将数据传递给 parse 方法。同样从 Scrapy 的框架来看,该函数的作用主要为:提取数据和生成下一个请求
爬虫构建完成之后,需要在终端执行命令来运行程序:
scrapy crawl spidername
通过上边的命令能够执行名为 spidername 的爬虫,但每次运行爬虫如果都需要在终端中执行命令的话,不断进行界面切换和代码调试都会显得比较麻烦,因此可以选择在项目目录下构建 py 文件,然后使用 cmdline 模块直接运行:
from scrapy import cmdline
cmdline.execute("scrapy crawl spidername".split())
这样就省掉了界面切换的步骤,也提高了代码调试的效率。
settings.py
该文件中包含了关于爬虫的一些设置,这里有几个选项需要进行设置:
- ROBOTSTXT_OBEY:设置为 False,否则为 True。True 表示遵守机器协议,此时爬虫会首先找 robots.txt 文件,如果找不到则会停止
- DEFAULT_REQUEST_HEADERS:默认请求头,可以在其中添加 User-Agent,表示该请求是从浏览器发出的,而不是爬虫
- DOWNLOAD_DELAY:表示下载的延迟,防止过快
- ITEM_PIPELINES:启用 pipelines.py
spiders
spiders 文件夹中的文件内容为:
# -*- coding: utf-8 -*-
import scrapy
from base.items import BaseItem
class QsbkSpider(scrapy.Spider):
name = 'qsbk_spider'
allowed_domains = ['www.qiushibaike.com']
start_urls = ['http://www.qiushibaike.com/text/page/1/']
base_url = 'http://www.qiushibaike.com'
def parse(self, response):
res_parse = response.xpath("//div[@class='col1 old-style-col1']/div")
for res in res_parse:
author = res.xpath("./div[@class='author clearfix']//img/@alt").get()
content = res.xpath(".//div[@class='content']//text()").getall()
content = ''.join(content).replace('\n','')
item = BaseItem(author=author,content=content)
yield item
next_url = self.base_url+response.xpath("//ul[@class='pagination']/li[last()]/a/@href").get()
if not next_url:
return
else:
yield scrapy.Request(next_url,callback=self.parse)
上边的程序主要分为两部分:
- for 循环内部为解析 response,该 response 来自 Downloader,然后将生成的 item 传递到 pipelines.py
- for 循环后的内容为生成下一个请求,然后通过 scrapy.Request 继续回调到 parse
items.py
# -*- coding: utf-8 -*-
# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html
import scrapy
class BaseItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
author = scrapy.Field()
content = scrapy.Field()
该文件中定义了数据模型,对应的是从 spider 中传回的 item。
pipelines.py
# -*- coding: utf-8 -*-
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
from scrapy.exporters import JsonLinesItemExporter
class BasePipeline(object):
def __init__(self):
self.fp = open('joke.json','wb')
self.exporter = JsonLinesItemExporter(self.fp,ensure_ascii=False,encoding='utf-8')
def open_spider(self,spider):
print('Spider start.')
def process_item(self, item, spider):
self.exporter.export_item(item)
return item
def close_spider(self,spider):
print('Spider end.')
上面的代码中存在三个固定含义的函数:
- open_spider:在 spider 开始执行之前先被执行
- process_item:spider 返回 item 后执行,可以进行数据的存储
- close_spider:在 spider 执行完毕之后执行
数据的存储可以采用 JSON,或者是 Scrapy.exporters 中的 JsonItemExporter,JsonLinesItemExporter 等,在 Scrapy.exporters 中定义了很多种数据存储的格式。
然后该项目就能够直接运行,爬取指定的网页信息了。