1.scrapy框架介绍
写一个爬虫,需要做很多的事情。比如:发送网络请求、数据解析、数据存储、反反爬虫机制(更换ip代理、设置请求头等)、异步请求等。这些工作如果每次都要自己从零开始写的话,比较浪费时间。因此Scrapy把一些基础的东西封装好了,在他上面写爬虫可以变的更加的高效(爬取效率和开发效率)。因此真正在公司里,一些上了量的爬虫,都是使用Scrapy框架来解决。
2.scrapy安装
-
pip install scrapy。
-
可能会出现问题:
- 在ubuntu下要先使用以下命令安装依赖包:
sudo apt-get install python3-dev build-essential python3-pip libxml2-dev libxslt1-dev zlib1g-dev libffi-dev libssl-dev,安装完成后再安装scrapy。 - 在windows下安装可能会提示
No module named win32api,这时候先使用命令:pip install pypiwin32,安装完成后再安装scrapy。 - 在windows下安装Scrapy可能会提示
twisted安装失败,那么可以到这个页面下载twisted文件:https://www.lfd.uci.edu/~gohlke/pythonlibs/,下载的时候要根据自己的Python版本来选择不同的文件。下载完成后,通过pip install xxx.whl
- 在ubuntu下要先使用以下命令安装依赖包:
-
官方文档:
中文:
[Scrapy 中文文档 — Scrapy 文档 (scrapy-16.readthedocs.io)](https://scrapy-16.readthedocs.io/zh_CN/1.6/)英文:
[Scrapy 2.9 documentation — Scrapy 2.9.0 documentation](https://docs.scrapy.org/en/latest/index.html)
3.scrapy框架架构
- Scrapy Engine(引擎):Scrapy框架的核心部分。负责在Spider和ItemPipeline、Downloader、Scheduler中间通信、传递数据等。
- Spider(爬虫):发送需要爬取的链接给引擎,最后引擎把其他模块请求回来的数据再发送给爬虫,爬虫就去解析想要的数据。这个部分是我们开发者自己写的,因为要爬取哪些链接,页面中的哪些数据是需要的,都是由程序员自己决定。
- Scheduler(调度器):负责接收引擎发送过来的请求,并按照一定的方式进行排列和整理,负责调度请求的顺序等。
- Downloader(下载器):负责接收引擎传过来的下载请求,然后去网络上下载对应的数据再交还给引擎。
- Item Pipeline(管道):负责将Spider(爬虫)传递过来的数据进行保存。具体保存在哪里,应该看开发者自己的需求。
- Downloader Middlewares(下载中间件):可以扩展下载器和引擎之间通信功能的中间件。
- Spider Middlewares(Spider中间件):可以扩展引擎和爬虫之间通信功能的中间件。
4.scrapy快速入门
- 创建项目
- 创建项目:
scrapy startproject [项目名称]. - 创建爬虫:
cd到项目中->scrapy genspider [爬虫名称] [域名].
- 项目中文件的作用
settings.py:用来配置爬虫的。middlewares.py:用来定义中间件。items.py:用来提前定义好需要下载的数据字段。pipelines.py:用来保存数据。scrapy.cfg:用来配置项目的。
- 项目结构
5.scrapy框架爬取古诗文网站实战
- 第一步
在settings.py文件里面将ROBOTSTXT_OBEY设置为False
ROBOTSTXT_OBEY = False
然后在DEFAULT_REQUEST_HEADERS里面设置请求头
DEFAULT_REQUEST_HEADERS = {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en",
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.63'
}
为了方便运行代码,在项目根目录下创建MyRun.py文件
from scrapy import cmdline
cmds = "scrapy crawl gsww_spider"
cmdline.execute(cmds.split(" "))
- 第二步
在gsww_spider.py文件里面编写提取数据得逻辑,细节见代码里面的注释
import scrapy
from scrapy.http.response.html import HtmlResponse # 鼠标右键,进入可以看到模块里面存在的那些方法
from scrapy.selector.unified import SelectorList # 鼠标右键,进入可以看到模块里面存在的那些方法
class GswwSpiderSpider(scrapy.Spider):
# 配置url请求的一些信息
name = "gsww_spider"
# 指定去爬取的网站
allowed_domains = ["gushiwen.cn"]
# 请求时,页面跳转到那个页面
start_urls = ["https://www.gushiwen.cn/default_1.aspx"]
# 为了方便学习,添加一个打印的方法
def myPrint(self, value):
print('*'*30)
print(value)
print('*' * 30)
def parse(self, response):
# self.myPrint(type(response)) scrapy.http.response.html.HtmlResponse
gsw_list = response.xpath('.//div[@class="left"]/div[@class="sons"]')
# self.myPrint(type(gsw_list)) <class 'scrapy.selector.unified.SelectorList'>
# response.xpath()返回的都是selectorList对象
# selectorList里面返回的都是selector对象
# selector.getall()方法获取selector对象里面指定的值
# selector.get()方法获取selector对象里面第一个值
for gws_div in gsw_list:
# 获取标题
title = gws_div.xpath(".//b/text()").get()
# self.myPrint(title)
# 获取朝代和姓名
sources = gws_div.xpath(".//p[@class='source']/a/text()").getall()
source = "".join(sources).split()
print(source)
# 获取内容
contents = gws_div.xpath(".//div[@class='contson']//text()").getall()
content = "".join(contents).split()
# print(content)
- 第三步
在items.py文件里面罗列出需要的数据
# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html
import scrapy
class GswwItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
title = scrapy.Field()
source = scrapy.Field()
content = scrapy.Field()
然后在gsww_spider.py文件里面导入Items.py,然后创建对象,传入参数。
from ..items import GswwItem
- 第四步
将组装后的数据发送给pipeline, 使用yield生成器返回给pipeline
yield item
- 第五步
在settings.py里面配置pipeline
ITEM_PIPELINES = {
"gsww.pipelines.GswwPipeline": 300,
}
在pipeline里面测试一下item传递过来了没用
- 第六步
编辑pipeline,让传递过来的数据存储到文件当中。
rom itemadapter import ItemAdapter
import json
class GswwPipeline:
# 爬虫开始的时候打开文件
def open_spider(self, spider):
self.fp = open('gsw_spider.txt', 'w', encoding='utf-8')
# 陆续写入内容到文件中
def process_item(self, item, spider):
self.fp.write(json.dump(dict(item)) + "\n")
return item
# 爬虫结束的时候关闭文件
def close_spider(self, spider):
self.fp.close()
- 第七步
获取多页数据
# 获取多页数据的请求
next_href = response.xpath("//a[@id='amore']/@href").get()
if next_href:
# urljoin()方法可以让网站域名和我们给定的值自动组成一个网址
next_url = response.urljoin(next_href)
request = response.Request(next_url)
yield request
5.gsww_spider.py里面完整的代码
# -*- coding: utf-8 -*-
import scrapy
from ..items import GswwItem
from scrapy.http.response.html import HtmlResponse
from scrapy.selector.unified import Selector
class GswwSpiderSpider(scrapy.Spider):
name = 'gsww_spider'
allowed_domains = ['gushiwen.org', 'gushiwen.cn']
start_urls = ['https://www.gushiwen.org/default_1.aspx']
def myprint(self,value):
print("="*30)
print(value)
print("="*30)
def parse(self, response):
# self.myprint(type(response))
# respsone.xpath返回的都是SelectorList对象
# SelectorList:里面存储的都是Selector对象
# SelectorList.getall:可以直接获取xpath中指定的值。
# SelectorList.get:可以直接提取第一个值。
gsw_divs = response.xpath("//div[@class='left']/div[@class='sons']")
for gsw_div in gsw_divs:
title = gsw_div.xpath('.//b/text()').get()
source = gsw_div.xpath(".//p[@class='source']/a/text()").getall()
try:
dynasty = source[0]
author = source[1]
# 下面的//text()代表的是获取class='contson'下的所有子孙文本
content_list = gsw_div.xpath(".//div[@class='contson']//text()").getall()
content = "".join(content_list).strip()
item = GswwItem(title=title,dynasty=dynasty,author=author,content=content)
yield item
exception:
print(title)
# next_href = response.xpath("//a[@id='amore']/@href").get()
# if next_href:
# next_url = response.urljoin(next_href)
# request = scrapy.Request(next_url)
# yield request
# 获取多页数据的请求
next_href = response.xpath("//a[@id='amore']/@href").get()
if next_href:
# urljoin()方法可以让网站域名和我们给定的值自动组成一个网址
next_url = response.urljoin(next_href)
request = response.Request(next_url)
yield request