python数据分析之爬虫基础:scrapy详解

devtools/2024/12/29 5:43:01/

一、爬虫工程化

在之前的爬虫学习中基本已经掌握了爬虫这门技术的大多数技术点,但是我们现在写的代码还很流程化,很难进行商用,想要爬虫达到商用级别,必须要对我们现在编写的爬虫进行大刀阔斧式的重组,以达到工程化的爬虫,所谓工程化,就是让程序更加有体系,有逻辑,更加模块化。

爬虫工程化:对爬虫的功能进行模块化的开发,并达到可以批量生产的效果(不论开发还是数据产出)

二、scrapy简介

scrapy是一个用python编写的开源网络爬虫框架,用于高效地从网站上抓取信息并提取结构化数据。

特点:速度快、简单、可扩展性强。

三、scrapy的工作流程

引擎:scrapy的核心,所有模块的衔接,数据流程处理。

调度器:本质上这东西可以看成是一个队列,里面存放着一堆我们即将要发送的请求。可以看成是一个url的容器,它决定了下一步要爬取哪一个url,在这里可以对url进行去重操作。

下载器:它的本质就是一个发送请求的模块,返回的是response对象。

爬虫:这是我们要写的的一个个部分的内容,负责解析下载器返回的response对象,从中提取我们需要的数据。

管道:这是我们要写的第二部分的内容,主要负责数据的存储和各种持久化操作。

4ba1d43cec8c43d6bacdb8b181dfd33c.jpeg

工作流程:

1、爬虫中起始的url构成的request对象,并传递给调度器。

2、引擎从调度器中获取到request对象,然后交给下载器

3、由下载器来获取到网页源代码,并封装成response对象,并回馈给引擎

4、引擎将获取到的response对象传递给spider,由spider对数据进行解析(parse),并回馈给引擎。

5、引擎将将数据传递给pipeline进行数据持久化保存或进一步的数据处理

6、在此期间如果spider中提取到的并不是数据,而是子页面url,可以进一步提交给调度器,进而重复步骤。

四、scrapy的安装

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple scrapy

查看是否安装成,可以在终端输入:

scrapy version

五、scrapy的使用

接下来,我们用scrapy来完成一个超级简单的爬虫,目标:深入理解scrapy的工作流程,以及各个模块之间是如何搭配工作的。

1、创建项目:

scrapy startproject 项目名称

示例:

scrapy startproject spider_test

创建好项目后,我们可以在pycharm中观察到scrapy帮我们创建了一个文件夹,结构如下:

2d427462a8ff4ca88c2f295c26bb05c5.png

以爬取4399小游戏的名称、类型为例子

# 首先进入项目
cd spider_test
# 创建爬虫项目
scrapy genspider xiao 4399.com # xaio代表名称后面跟的是域名

然后spiders文件夹下会生成一个xiao.py的文件,内容为:

import scrapyclass XiaoSpider(scrapy.Spider):name = "xiao" # 爬虫的名字allowed_domains = ["4399.com"] # 允许抓取的域名start_urls = ["https://www.4399.com/flash/"] # 起始页面urldef parse(self, response):# 该方法是用来处理解析的print(response)

运行该程序:

scrapy crawl xiao

但会发现有相当多的内容日志,我们可以在setting.py加上这段程序:

LOG_LEVEL = "WARNING"
# 日志的级别:DEBUG INFO WARNING ERROR CRITICAL(由低到高)设置成warning代表warning及以上的信息才能被打印

运行程序后,信息如下:

<200 https://www.4399.com/flash/>
(.venv) PS D:python学习python_studypythonProjectspider_test> import scrapyclass XiaoSpider(scrapy.Spider):name = "xiao" # 爬虫的名字allowed_domains = ["4399.com"] # 允许抓取的域名start_urls = ["https://www.4399.com/flash/"] # 起始页面urldef parse(self, response):# 该方法是用来处理解析的# print(response)# 拿到源代码# print(response.text)# 提取数据# text = response.xpath('//ul[@class="n-game cf"]/li/a/b/text()').extract()# print(text)# 分块解析数据li_list= response.xpath('//ul[@class="n-game cf"]/li')for li in li_list:name = li.xpath('a/b/text()').extract_first()category = li.xpath('em/a/text()').extract_first()time = li.xpath('em/text()').extract_first()dic = {"name":name,"category":category,"time":time}

对数据进行解析后,下一步就是对数据进行存储,这一步是在pipeline管道中进行的,需要用到yield将数据传递个管道。

yield dic # 可以节省内存,是因青睐调用的

因为管道默认是不生效的,需要在setting里手动开启管道

ITEM_PIPELINES = {# key就是管道的路径# value是管道的优先级,数值越小,优先级越高"spider_test.pipelines.SpiderTestPipeline": 300,
}

管道pipeline.py文件中打印数据和爬虫的名称:

class SpiderTestPipeline:def process_item(self, item, spider): # 处理数据的专用方法,item;数据,spider:爬虫print(item)print(spider.name)return item

对url进行代理,cookie以及UA等操作可以在爬虫和引擎之间或者引擎和下载器之间处理。

六、scrapy shell

scrapy shell是scrapy终端,是一个交互终端,可以在未启动spider的情况下尝试及调试爬取的代码。其本意是是用来测试提取数据的代码,不过可以将其是为正常的python终端,在上面测试任何的python代码。该终端是用来测试xpath或css表达式,查找他们的工作及爬取的网页中提取的数据,在编写spider时,该终端提供了交互性测试表达式代码的功能,免去了每次修改后运行spider的麻烦。

python_159">1、安装ipython

pip install ipython

ipython终端与其他相比更为强大,提供智能的自动补全,高亮输出,及其他特性。

2、应用

在ipython终端直接输入:

scrapy shell www.baidu.com

3、语法

(1)response对象

response.body(二进制文本)

response.text

response.url

response.stayus

(2)response解析

response.xpath()

使用xpath路径查询特定元素,返回selector列表对象

response.css()(bs4语法)

获取内容:response.css(‘#su::text’).extract_first()

获取属性:response.css(‘#su::attr(“value”)’).extract_first()

(3)selector对象

extract()

提取selector对象的值,如果提取不到,则会报错

使用xpath请求到的对象是一个selector对象,需要使用extract()方法拆包

extract_first()

提取selector列表中的第一个值,若提取不到返回空值

以获取百度网页百度一下为例:

scrapy shell www.baidu.com
response.xpath('//input[@id="su"]/@value').extract_first()

直接在终端输入即可,不需要输入ipython

七、yield

1、带有yeild的函数不再是一个普通函数,而是一个生成器generator,用于迭代。

2、yield是一个类似于return的关键字,迭代一次遇到yield时就返回后面的值。重点是:下一次迭代时,从上一次迭代遇到的yield后面的代码开始执行。

简要理解:yield就是return一个返回值,并记住这个返回的位置,下次迭代就从这个位置后开始。

案例:当当网(1)yield (2)管道封装(3)多条管道下载(4)多页数据下载

首先需要在终端创建项目以及爬虫名称

scrapy startproject scrapy_dangdang

cd scrapy_dangdang

scrapy genspider dangdang www.dangdang.com

因为要爬取书的名称、图片以及价格,可以在items.py中自定义数据结构。

class ScrapyDangdangItem(scrapy.Item):# define the fields for your item here like:# name = scrapy.Field()# 图名src = scrapy.Field()# 名称name = scrapy.Field()# 价格price = scrapy.Field()

在dangdang.py中去执行解析数据并发送url给引擎

import scrapy
from scrapy_dangdang.items import ScrapyDangdangItemclass DangdangSpider(scrapy.Spider):name = "dangdang"allowed_domains = ["www.dangdang.com"]start_urls = ["https://category.dangdang.com/cp01.01.02.00.00.00.html"]def parse(self, response):# src = //ul[@id="component_59"]/li//img/@src# name = //ul[@id="component_59"]/li//img/@alt# price = //ul[@id="component_59"]/li//p[@class="price"]/span[1]/text()# 所有的seletor的对象,都可以再次调用xpath方法li_list = response.xpath('//ul[@id="component_59"]/li')for li in li_list:src = li.xpath('.//img/@data-original').extract_first() # 这里需要注意:除了第一条数据没有懒加载,其它均有懒加载if src:src = srcelse:src = li.xpath('.//img/@src').extract_first()name = li.xpath('.//img/@alt').extract_first()price = li.xpath('.//p[@class="price"]/span[1]/text()').extract_first()book = ScrapyDangdangItem(src=src, name=name, price=price)yield book # 需要在setting中手动开启pipeline管道

然后就需要保存数据,这个时候就用到了管道pipeline,我们需要在setting中手动打开管道。

ITEM_PIPELINES = {"scrapy_dangdang.pipelines.ScrapyDangdangPipeline": 300,
}

key就是管道的路径,value是管道的优先级,数值越小,优先级越高。

在pipeline中保存数据:

from itemadapter import ItemAdapterclass ScrapyDangdangPipeline:# 在爬虫文件执行之前,就执行的方法:def open_spider(self, spider):self.fp = open("book.json","w",encoding="utf-8") # 打开文件但并未关闭,所以w的模式写入def process_item(self, item, spider):# item就是我们的数据# with open("book.json", "a", encoding="utf-8") as f: # 这里需要采用追加的形式,因为数据是一条条传递来的,每一个对象都打开一次文件,从而导致数据被覆盖#     f.write(str(item))# 但上述方法并不是最优解,因为我们对文件的操作过于频繁,,会有频繁的IO操作。这个时候我们通过两个方法来改进代码self.fp.write(str(item)    )return item# 爬虫文件执行后,执行的方法。def close_spider(self,spider):self.fp.close()

多条管道下载,实现一边下载图片,一边下载json数据。

# setting文件中需要加上这条管道。因为图片下载慢,因此优先级就低一些
ITEM_PIPELINES = {"scrapy_dangdang.pipelines.ScrapyDangdangPipeline": 300,"scrapy_dangdang.pipelines.DangDangDownloadImg":301
}
# pipelines文件
import urllib.request
class DangDangDownloadImg:def process_item(self, item, spider):url = "http:" + item.get('src') # 这里需要注意的是:下载下来的路径前面无http,需要拼接filename = './books/'+item.get("name")+'.jpg'urllib.request.urlretrieve(url=url,filename=filename)return item

完成当当网的多页数据下载,因为每一页爬取的业务都是一样的,所以我们只需要将执行的那个页的请求再次调用parse方法即可。

import scrapy
from scrapy_dangdang.items import ScrapyDangdangItem
from scrapy.http import Request
class DangdangSpider(scrapy.Spider):name = "dangdang"# 如果十多页下载的话,那么必须要调整的是allowed_domins的范围,一般情况只写域名allowed_domains = ["category.dangdang.com"]start_urls = ["https://category.dangdang.com/pg1-cp01.01.02.00.00.00.html"]base_url = "https://category.dangdang.com/pg"page = 1def parse(self, response):# src = //ul[@id="component_59"]/li//img/@src# name = //ul[@id="component_59"]/li//img/@alt# price = //ul[@id="component_59"]/li//p[@class="price"]/span[1]/text()# 所有的seletor的对象,都可以再次调用xpath方法li_list = response.xpath('//ul[@id="component_59"]/li')for li in li_list:src = li.xpath('.//img/@data-original').extract_first() # 这里需要注意:除了第一条数据没有懒加载,其它均有懒加载if src:src = srcelse:src = li.xpath('.//img/@src').extract_first()name = li.xpath('.//img/@alt').extract_first()price = li.xpath('.//p[@class="price"]/span[1]/text()').extract_first()book = ScrapyDangdangItem(src=src, name=name, price=price)yield book # 需要在setting中手动开启pipeline管道if self.page < 100:self.page = self.page + 1url = self.base_url + str(self.page) + "-cp01.01.02.00.00.00.html"# 调用parse方法,callback表示要执行的函数yield scrapy.Request(url=url, callback=self.parse)

八、CrawlSpider

CrawlSpider可以定义规则,在解析html内容的时候,可以根据连接规则提出指定的链接,然后再向这些链接发送请求。所以,如果有需要跟进链接的需求,意思就是爬取了网页之后,需要提取链接再次爬取,使用CrawlSpider是非常合适的。

1、提取链接

提取链接器,在这里就可以写规则提取指定连接

scrapy.linkectractors.LinkExtractor(

allow = (), # 正则表达式,提取符合正则的链接

dengy = (), # (不用)正则表达式 不提取符合正则的链接

allow_domins = (), # (不用)允许的域名

restrict_xpaths = (), # xpath,提取符合xpath规则的链接

restrict_css = (), # 提取符合选择器规则的链接

2、模拟使用

正则用法:links = LinkExtractor(allow=r"list_23d+.html")

xpath语法:links = LinkExtractor(restrict_xpaths=r"//div[@class=‘x’]")

css语法:links = LinkExtractor(restrict_css=“.x”)

3、提取连接

link.extract_links(response)

案例:以爬取多页读书网数据为例(每一页数据的结构是相似的),并存储到数据库中。

4d25e838334c4f3b828495da7e0d822a.png

scrapy shell https://www.dushu.com/book/1107.html
# 导包
In [1]: from scrapy.linkextractors import LinkExtractor
# 通过re正则表达式提取数据
In [2]: link = LinkExtractor(allow=r'/book/1107_d+.html')
# 提取连接
In [3]: link.extract_links(response)

9440a84fd9cf453a98654afcac23eb57.png

注意事项:

1、callback只能写函数名字符串,callback=“parse_item”

2、在基本的spider中,如果重新发送请求,那么callback写的是 callback=self.parse_item follow=true 是否跟进就是按照提取连接规则进行提取

代码复现:

# 在终端创建项目
scrapy stratproject scrapy_readbook
# 进入根目录
scrapy startproject scrapy_readbook
# 创建爬虫文件这里是有所不同的,需要注意
scrapy genspider -t crawl read www.dushu.com/book/1107.html

80c88904007a4c0ea8afde366e2f8645.png

上面是原始的read爬虫文件,还是有所不同的。

把数据存储在MySQL中,这里可能需要一些MySQL以及pymysql的知识。可以根据情况进行学习。这里我们只需要在pipelines文件中写主要的存储逻辑,并且在settings文件中进行多管道下载:

# settings文件
DB_HOST = "localhost"
DB_PORT = 3306
DB_USER = "root"
DB_PASSWORD = "0219423"
ITEM_PIPELINES = {"scrapy_readbook.pipelines.ScrapyReadbookPipeline": 300,"scrapy_readbook.pipelines.PyMysqlPipeline":301
}
# pipelines文件
# 加载settings文件
from scrapy.utils.project import get_project_settings
from pymysql import Connection
class PyMysqlPipeline:def open_spider(self, spider):settings = get_project_settings()self.host = settings["DB_HOST"]self.port = settings["DB_PORT"]self.user = settings["DB_USER"]self.password = settings["DB_PASSWORD"]self.connect()def connect(self):self.conn = Connection(host=self.host,port=self.port,user=self.user,password=self.password)self.cursor = self.conn.cursor()self.conn.select_db("test")def process_item(self, item, spider):sql = 'insert into spider(name,src) values("{}","{}")'.format(item['name'], item['src'])self.cursor.execute(sql)self.conn.commit()return itemdef close_spider(self, spider):self.cursor.close()self.conn.close()

ebcfbba868ff469db8ebaa10f7e72ce1.png

九、scrapy的post请求

我们还是以百度翻译为例,在这里我就只写出逻辑执行的文件:

import scrapy
import jsonclass PostSpider(scrapy.Spider):name = "post"allowed_domains = ["fanyi.baidu.com"]# post请求 如果没有参数 那么这个请求将没有任何意义,所以start_urls也就没有用了,从而parse方法也无用# start_urls = ["https://fanyi.baidu.com/sug"]# def parse(self, response):#     passdef start_requests(self):url = "https://fanyi.baidu.com/sug"data={"kw": "spider"}yield scrapy.FormRequest(url=url, formdata=data, callback=self.parse_second)def parse_second(self, response):content = response.textobj = json.loads(content)print(obj)

http://www.ppmy.cn/devtools/146313.html

相关文章

Oracle 11G还有新BUG?ORACLE 表空间迷案!

前段时间遇到一个奇葩的问题&#xff0c;在开了SR和oracle support追踪两周以后才算是有了不算完美的结果&#xff0c;在这里整理出来给大家分享。 1.问题描述 12/13我司某基地MES全厂停线&#xff0c;系统卡死不可用&#xff0c;通知到我排查&#xff0c;查看alert log看到是…

Java 构建工具的演变与比较:Ant、Maven 和 Gradle 的发展历程

目录 引言Java 构建工具的背景Ant 的诞生与特点 3.1 Ant 的发展历程3.2 Ant 的核心特性3.3 Ant 的不足之处 Maven 的崛起 4.1 Maven 的发展历程4.2 Maven 的核心特性4.3 Maven 的局限性 Gradle 的出现与优势 5.1 Gradle 的发展历程5.2 Gradle 的核心特性5.3 Gradle 的优势与挑…

RustDesk远程及自建服务器搭建教程

要开始使用RustDesk远程和自建服务器&#xff0c;你需要遵循以下步骤&#xff1a; 下载和安装RustDesk&#xff1a;RustDesk是一款开源的远程支持应用程序。你可以在其官方网站&#xff08;https://rustdesk.com/&#xff09;上下载适用于你的操作系统的安装程序。安装过程非常…

uniapp 判断多选、选中取消选中的逻辑处理

一、效果展示 二、代码 1.父组件: :id=“this.id” : 给子组件传递参数【id】 @callParentMethod=“takeIndexFun” :给子组件传递方法,这样可以在子组件直接调用父组件的方法 <view @click="$refs.member.open()"

Max AI prompt1

1&#xff0c;内容/要点逻辑链&#xff0c;层次结构可视化 请提取其中的主要内容以及观点&#xff0c;以及对应的逻辑链&#xff0c;以图示化、层次结构通俗易懂地展现&#xff0c;要求使用中文 #我目前常用的文献阅读prompt提示词&#xff0c;主要是内容、逻辑链2者兼备2&…

Ollama-OCR:利用视觉语言模型从图像中提取文本

Ollama-OCR利用视觉语言模型从图像中提取文本。 本文将介绍 Ollama-OCR 的关键特点、安装方法、快速开始指南以及输出格式的详细信息。 github:https://github.com/imanoop7/Ollama-OCR 需安装:ollama https://ollama.com/download 特点 支持多种视觉模型&#xff1a;Ollama…

Alma linux部署gitlab

前提条件 操作系统: AlmaLinux 8&#xff08;或相似版本&#xff09;内存: 至少 4GB RAM硬盘: 至少 20GB 硬盘空间&#xff08;建议更多&#xff09;sudo 权限: 你需要在系统上具有 root 权限&#xff0c;或者是可以使用 sudo 的用户。 步骤 1: 更新系统 首先&#xff0c;更…

mapbox基础,加载mapbox官方地图

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;mapbox 从入门到精通 文章目录 一、&#x1f340;前言1.1 ☘️mapboxgl.Map 地图对象…