python爬虫--scrapy框架

请添加图片描述
Scrapy

一 介绍

Scrapy简介

1.Scrapy是用纯Python实现一个为了爬取网站数据、提取结构性数据而编写的应用框架,用途非常广泛2.框架的力量,用户只需要定制开发几个模块就可以轻松的实现一个爬虫,用来抓取网页内容以及各种图片,非常之方便
Scrapy主要包括了以下组件:1.引擎(Scrapy)用来处理整个系统的数据流处理, 触发事务(框架核心)2.调度器(Scheduler)用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址3.下载器(Downloader)用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)4.爬虫(Spiders)爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面5.项目管道(Pipeline)负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。6.下载器中间件(Downloader Middlewares)位于Scrapy引擎和下载器之间的框架,主要是处理Scrapy引擎与下载器之间的请求及响应。7.爬虫中间件(Spider Middlewares)介于Scrapy引擎和爬虫之间的框架,主要工作是处理蜘蛛的响应输入和请求输出。8.调度中间件(Scheduler Middewares)介于Scrapy引擎和调度之间的中间件,从Scrapy引擎发送到调度的请求和响应。

Scrapy运行流程

1.引擎从调度器中取出一个链接(URL)用于接下来的抓取,包括过滤器和对列,过滤后的url交给对列
2.引擎把URL封装成一个请求(Request)传给下载器
3.下载器把资源下载下来,并封装成应答包(Response)
4.爬虫解析Response
5.解析出实体(Item),则交给引擎,在提交到管道进行进一步的处理(持久化存储处理)
6.解析出的是链接(URL),则把URL交给调度器等待抓取
都会经过引擎进行调度

二 安装

#Windows平台1、pip3 install wheel #安装后,便支持通过wheel文件安装软件,wheel文件官网:https://www.lfd.uci.edu/~gohlke/pythonlibs3、pip3 install lxml4、pip3 install pyopenssl5、下载并安装pywin32:https://sourceforge.net/projects/pywin32/files/pywin32/6、下载twisted的wheel文件:http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted7、执行pip3 install 下载目录\Twisted-17.9.0-cp36-cp36m-win_amd64.whl8、pip3 install scrapy#Linux平台1、pip3 install scrapy

三 命令行工具

介绍

#1 查看帮助scrapy -hscrapy <command> -h#2 有两种命令:其中Project-only必须切到项目文件夹下才能执行,而Global的命令则不需要Global commands: #全局命令startproject #创建项目genspider    #创建爬虫程序 # 示例 scrapy genspider  baidu     www.baidu.com settings     #如果是在项目目录下,则得到的是该项目的配置runspider    #运行一个独立的python文件,不必创建项目shell        #scrapy shell url地址  在交互式调试,如选择器规则正确与否fetch        #独立于程单纯地爬取一个页面,可以拿到请求头view         #下载完毕后直接弹出浏览器,以此可以分辨出哪些数据是ajax请求version      #scrapy version 查看scrapy的版本,scrapy version -v查看scrapy依赖库的版本Project-only commands: #项目文件下crawl        #运行爬虫,必须创建项目才行,确保配置文件中ROBOTSTXT_OBEY = Falsecheck        #检测项目中有无语法错误list         #列出项目中所包含的爬虫名edit         #编辑器,一般不用parse        #scrapy parse url地址 --callback 回调函数  #以此可以验证我们的回调函数是否正确bench        #scrapy bentch压力测试#3 官网链接https://docs.scrapy.org/en/latest/topics/commands.html

示例

#1、执行全局命令:请确保不在某个项目的目录下,排除受该项目配置的影响
scrapy startproject MyProject(项目名)cd MyProject (切换到项目下)
scrapy genspider baidu www.baidu.com  #创建爬虫程序#(baidu)爬虫名    对应相关域名(www.baidu.com可以先随便写个www.xxx.com ) #代表这个爬虫程序只能爬取www.baidu.com 这个域名或者百度的子域名scrapy settings --get XXX #如果切换到项目目录下,看到的则是该项目的配置scrapy runspider baidu.py #执行爬虫程序scrapy shell https://www.baidu.comresponseresponse.statusresponse.bodyview(response)scrapy view https://www.taobao.com #如果页面显示内容不全,不全的内容则是ajax请求实现的,以此快速定位问题scrapy fetch --nolog --headers https://www.taobao.comscrapy version #scrapy的版本scrapy version -v #依赖库的版本#2、执行项目命令:切到项目目录下
scrapy crawl baidu
scrapy check
scrapy list
scrapy parse http://quotes.toscrape.com/ --callback parse
scrapy bench

四 项目结构以及爬虫应用简介

目录结构

project_name/scrapy.cfgproject_name/__init__.pyitems.pypipelines.pysettings.pyspiders/__init__.py爬虫1.py爬虫2.py爬虫3.py

应用说明

scrapy.cfg:爬虫项目的配置文件。__init__.py:爬虫项目的初始化文件,用来对项目做初始化工作。items.py:爬虫项目的数据容器文件,用来定义要获取的数据。pipelines.py:爬虫项目的管道文件,用来对items中的数据进行进一步的加工处理。settings.py:爬虫项目的设置文件,包含了爬虫项目的设置信息。middlewares.py:爬虫项目的中间件文件,

pycharm中运行爬虫程序

在项目目录先创建entrypoint.py文件,文件名不能变
from scrapy.cmdline import execute
execute(['scrapy','crawl','baidu','--nolog']) #百度为爬虫名,列表的前两项不变 --nolog可写可不写,作用是不在打印其他配置项,只打印需要的内容

五 Spiders

1.介绍

1.Spider是由一系列类(定义了一个网址一组网址将别爬取)组成,具体包括了如何执行爬取任务并且如何从页面中提取结构化的数据
2.Spider是你为了一个特定的网址或一组网址自定义爬取或解析页面行为的地方

2.Spider会循环做的事情

#1、生成初始的Requests来爬取第一个URLS,并且标识一个回调函数
第一个请求定义在start_requests()方法内默认从start_urls列表中获得url地址来生成Request请求,默认的回调函数是parse方法。回调函数在下载完成返回response时自动触发#2、在回调函数中,解析response并且返回值
返回值可以4种:包含解析数据的字典Item对象新的Request对象(新的Requests也需要指定一个回调函数)或者是可迭代对象(包含Items或Request)#3、在回调函数中解析页面内容
通常使用Scrapy自带的Selectors,但很明显你也可以使用Beutifulsoup,lxml或其他你爱用啥用啥。#4、最后,针对返回的Items对象将会被持久化到数据库
通过Item Pipeline组件存到数据库:https://docs.scrapy.org/en/latest/topics/item-pipeline.html#topics-item-pipeline)
或者导出到不同的文件(通过Feed exports:https://docs.scrapy.org/en/latest/topics/feed-exports.html#topics-feed-exports)

3.爬取格式

entrypoint.py

from scrapy.cmdline import execute
execute(['scrapy','crawl','amazon1','-a','keywords=iphone8手机','--nolog'])
加参数的格式
# -*- coding: utf-8 -*-
import scrapy
from urllib.parse import urlencodeclass Amazon1Spider(scrapy.Spider):name = 'amazon1'allowed_domains = ['www.amazon.cn']   #也可以不用,写的话就会对请求的url进行限制,下边的start_urls就只能请求allowed_domains中urlstart_urls = ['https://www.amazon.cn/'] #可以放多个请求的url#不写allowed_domains,就可以在start_urls请求列表中写多个url# 自定义的配置,可以加请求头系列的配置,先从这里找,没有去settings中找custom_settings = {'REQUEST_HEADERS':{'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36',}}# 规定外部传进来的参数def __init__(self,keywords,*args,**kwargs):super(Amazon1Spider,self).__init__(*args,**kwargs)self.keywords=keywords# 发送请求def start_requests(self):url = 'https://www.amazon.cn/s?%s&ref=nb_sb_noss_1' %(urlencode({'k':self.keywords}))yield scrapy.Request(url=url,callback=self.parse,dont_filter=True) #请求方式GET#post请求 : scrapy.FormRequest(url,formdata=data,callback)# 解析def parse(self, response):detail_url = response.xpath('//*[@id="search"]/div[1]/div[2]/div/span[4]/div[1]/div[1]/div/span/div/div/div[2]/div[3]/div/div/h2/a/@href') #如果这里得到多个url,商品详情url求,通过for循环再次对详情的url返送请求,每次返送请求必须有对应的回调函数for url in detail_url: yiled yield scrapy.Request(url=url,callback=self.parse_detaildont_filter=True)print(detail_url)def parse_detail(self,response):print(response) #详情页的响应结果def close(spider, reason): #解析之后执行closeprint('结束')

4.示例

爬取三国演义的文章标题以及每篇文章的内容
from scrapy.cmdline import execute
execute(['scrapy','crawl','sang','--nolog'])
# -*- coding: utf-8 -*-
import scrapy
class SangSpider(scrapy.Spider):name = 'sang'allowed_domains = ['www.shicimingju.com/book/sanguoyanyi.html']start_urls = ['http://www.shicimingju.com/book/sanguoyanyi.html/']custom_settings = { #加一些请求头信息'REQUEST_HEADERS':{'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36',}}def start_requests(self):url = 'http://www.shicimingju.com/book/sanguoyanyi.html/'yield scrapy.Request(url=url,callback=self.parse,#解析主页面的回调函数) #向主页面发起请求def parse(self, response): #解析主页面的响应信息all_urls = response.xpath('/html/body/div[4]/div[2]/div[1]/div/div[4]/ul/li/a/@href').extract()#所有的文章的url all_title = response.xpath('/html/body/div[4]/div[2]/div[1]/div/div[4]/ul/li/a/text()').extract()#所有标题for url in all_urls:# print(response.urljoin(url)) #拼接url detail_urls = 'http://www.shicimingju.com'+urlprint(detail_urls)yield scrapy.Request(url=detail_urls,  callback=self.parse_detail,#解析详情页面的回调函数dont_filter=True) #向详细文章发送请求def parse_detail(self,response):content = response.xpath('/html/body/div[4]/div[2]/div[1]/div[1]/div/p/text()') #解析详情文章的响应print(content)

5.数据解析

- scrapy中封装的xpath的方式进行数据解析。
- scrapy中的xpath和etree中的xpath的区别是什么?etree中的xpath返回的是字符串或列表scrapy的xpath进行数据解析后返回的列表元素为Selector对象,然后必须通过extract或者extract_first这两个方法将Selector对象中的对应数据取出extract_first:将列表元素中第0个selector对象提取
extract:取出列表中的每一个selector对象提取

6.数据持久化存储

基于终端的指令存储
特性:只能将parse方法的返回值存储到本地的磁盘文件中
指令:scrapy crawl 爬虫程序名 -o 文件名
局限性:只能存储到磁盘中,存储的文件名有限制,只能用提供的文件名
示例
# 爬取糗事百科笑话的标题和内容
import scrapy
class QiubSpider(scrapy.Spider):name = 'qiub'start_urls = ['http://www.lovehhy.net/Joke/Detail/QSBK/']def parse(self, response):all_data = []#response.xpath拿到的是个列表里面是Selector对象title = response.xpath('//*[@id="footzoon"]/h3/a/text()').extract()content = response.xpath('//*[@id="endtext"]//text()').extract() #因为在内容中有对个br标签进行分割,所以用//text()title=''.join(title)content=''.join(content)dic = {'title':title,'content':content}all_data.append(dic)return all_data #必须有返回值才能用基于终端指令的存储
终端存储指令:scrapy crawl qiub -o qiubai.csv(csv是提供的文件类型)    
基于管道存储
实现流程
- 基于管道:实现流程1.数据解析2.在item类中定义相关的属性3.将解析的数据存储或者封装到一个item类型的对象(items文件中对应类的对象)4.向管道提交item5.在管道文件的process_item方法中接收item进行持久化存储6.在配置文件中开启管道管道只能处理item类型的对象
文件形式
示例

qiub.py

# -*- coding: utf-8 -*-
import scrapy
from qiubai.items import QiubaiItemclass QiubSpider(scrapy.Spider):name = 'qiub'start_urls = ['http://www.lovehhy.net/Joke/Detail/QSBK/']def parse(self, response):all_data = []title = response.xpath('//*[@id="footzoon"]/h3/a/text()').extract()content = response.xpath('//*[@id="endtext"]//text()').extract()title=''.join(title)content=''.join(content)dic = {'title':title,'content':content}all_data.append(dic)item = QiubaiItem() #实例化一个item对象item['title'] = title #封装好的数据结构item['content'] = contentyield item #向管道提交item,提交给优先级最高的管道类

items.py

import scrapy
class QiubaiItem(scrapy.Item):# define the fields for your item here like:title = scrapy.Field() #Fieid可以接受任意类型的数据格式/类型content = scrapy.Field()

pipelines.py

class QiubaiPipeline(object):f= Noneprint('开始爬虫.....')def open_spider(self,spider): # 重写父类方法,开启文件self.f = open('qiubai.txt','w',encoding='utf-8')def close_spider(self,spider):# 重写父类方法,关闭文件print('结束爬虫')self.f.close()def process_item(self, item, spider):#item是管道提交过来的item对象title = item['title']  #取值content = item['content']self.f.write(title+':'+content+'\n') #写入文件return item  

settings.py

# 开启管道,可以开启多个管道
ITEM_PIPELINES = {'qiubai.pipelines.QiubaiPipeline': 300, #300表示的优先级
}
将同一份数据持久化到不同的平台
1.管道文件中的一个管道类负责数据的一种形式的持久化存储
2.爬虫文件向管道提交的item只会提交给优先级最高的那一个管道类
3.在管道类的process_item中的return item表示的是将当前管道接收的item返回/提交给下一个即将被执行的管道类
数据库(mysql)
示例
import pymysql
# 负责将数据存储到mysql
class MysqlPL(object):conn = Nonecursor = Nonedef open_spider(self, spider):self.conn = pymysql.Connect(host='127.0.0.1', port=3306, user='root', password='123', db='spider',charset='utf8')print(self.conn)def process_item(self, item, spider):author = item['author']content = item['content']sql = 'insert into qiubai values ("%s","%s")' % (author, content)self.cursor = self.conn.cursor()try:self.cursor.execute(sql) #执行正确的话就提交事务self.conn.commit()except Exception as e:print(e)self.conn.rollback()  #回滚,如果出现错误就回滚return itemdef close_spider(self, spider):self.cursor.close()self.conn.close()

7.爬取校花网图片

ImagesPipeline的简介
专门爬取图片的管道1.爬取一个Item,将图片的URLs放入image_urls字段
2.从Spider返回的Item,传递到Item Pipeline
3.当Item传递到ImagePipeline,将调用Scrapy 调度器和下载器完成4.image_urls中的url的调度和下载。
5.图片下载成功结束后,图片下载路径、url和校验和等信息会被填充到images字段中。
ImagesPipeline的使用:
from scrapy.pipelines.images import ImagesPipeline
import scrapy
# 通过重写父类方法
class SpiderImgPipeline(ImagesPipeline):# 对某一个媒体资源进行请求发送# item是提交过来的item(src)def get_media_requests(self, item, info): yield scrapy.Request(item['src'])# 制定媒体数据存储的名称def file_path(self, request, response=None, info=None):img_name = request.url.split('/')[-1]print(img_name+'正在爬取')return img_name# 将item传递个下一个即将被执行的管道类def item_completed(self, results, item, info):return item# 在配置中添加图片的存储路径
IMAGES_STORE = './imgslib'
爬取示例:

settings.py

IMAGES_STORE = './imgslib' #存储爬取到的图片
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36'
ROBOTSTXT_OBEY = False
LOG_LEVEL = 'ERROR'
ITEM_PIPELINES = {'spider_img.pipelines.SpiderImgPipeline': 300,
}

img.py

# -*- coding: utf-8 -*-
import scrapy
from spider_img.items import SpiderImgItemclass ImgSpider(scrapy.Spider):name = 'img'start_urls = ['http://www.521609.com/daxuexiaohua/']url= 'http://www.521609.com/daxuexiaohua/list3%d.html' #通用的url模板,需要加page页数,每个页数的模板page_num = 1def parse(self, response):li_list = response.xpath('//*[@id="content"]/div[2]/div[2]/ul/li')for li in li_list:img_src = 'http://www.521609.com/' + li.xpath('./a[1]/img/@src').extract_first() #获取的所有的图片的url,extract_first获取的字符串类型,extract获取的是list,里面存了selector对象item = SpiderImgItem() #实例化item对象item['src'] = img_src  #把数据添加到item中yield item #提交到itemif self.page_num < 3: #爬取的前两页self.page_num += 1new_url = format(self.url%self.page_num)  #其他页数的urlyield scrapy.Request(new_url,callback=self.parse)  #对页数发请求

items.py

import scrapy
class SpiderImgItem(scrapy.Item):src = scrapy.Field()pass

piplines.py

from scrapy.pipelines.images import ImagesPipeline
import scrapyclass SpiderImgPipeline(ImagesPipeline):# 对某一个媒体资源进行请求发送# item是提交过来的item(src)def get_media_requests(self, item, info):yield scrapy.Request(item['src'])# 制定媒体数据存储的名称def file_path(self, request, response=None, info=None):img_name = request.url.split('/')[-1]print(img_name+'正在爬取')return img_name# 将item传递个下一个即将被执行的管道类def item_completed(self, results, item, info):return item

8.请求传参(深度爬取)

示例:爬取4567tv电影网的电影信息

movie.py

# 需求:爬取电影网的电影名称和电影的简介
# 分析:去电影网的首页请求会看到所有的电影,但不能获取简介,需要现获取所有的电影的url,在对每个电影的url反请求,对电影详情发请求获取简介import scrapy
from movie_spider.items import MovieSpiderItemclass MovieSpider(scrapy.Spider):name = 'movie'start_urls = ['https://www.4567tv.tv/index.php/vod/show/class/%E5%8A%A8%E4%BD%9C/id/1.html']page_num = 1def parse(self, response):movie_list = response.xpath('/html/body/div[1]/div/div/div/div[2]/ul/li')for movie_li in movie_list:movie_title = movie_li.xpath('./div[1]/a/@title').extract_first()movie_url = 'https://www.4567tv.tv'+movie_li.xpath('./div[1]/a/@href').extract_first()item = MovieSpiderItem()item['movie_title'] = movie_titleyield scrapy.Request(movie_url,callback=self.parse_movie_detail,meta={'item':item})#请求传参if self.page_num < 5:self.page_num += 1new_url = f'https://www.4567tv.tv/index.php/vod/show/class/%E5%8A%A8%E4%BD%9C/id/1/page/{self.page_num}.html'yield scrapy.Request(url=new_url,callback=self.parse,meta={'item':item})def parse_movie_detail(self,response):item = response.meta['item']#取出item,接收movie_about = response.xpath('/html/body/div[1]/div/div/div/div[2]/p[5]/span[2]/text()').extract_first()item['movie_about'] = movie_about #电影的简介yield item# 深度爬取(传参):在电影首页获取不到电影的简介,在最后提交item时,电影的标题和电影的简介在不同的解析回调函数中,所以先把标题放在item中(但是不提交),传递给下一个回调函数,继续用item,向item中存入要提交的数据,!!!# 为什么不能设置为全局?# 因为在最后提交yield item时会产生数据覆盖现象item['movie_title'] = movie_title #存
meta={'item':item}   # meta参数传递
item = response.meta['item'] #取

items.py

import scrapyclass MovieSpiderItem(scrapy.Item):# define the fields for your item here like:movie_title = scrapy.Field()movie_about = scrapy.Field()pass

pipelines.py

import pymysql
class MovieSpiderPipeline(object):conn = Nonecursor = Nonedef open_spider(self, spider):self.conn = pymysql.Connect(host='127.0.0.1', port=3306, user='root', password='123', db='movie_spider',charset='utf8')def process_item(self, item, spider):movie_title = item['movie_title']movie_about = item['movie_about']sql = 'insert into movie_info values ("%s","%s")' % (movie_title, movie_about)self.cursor = self.conn.cursor()try:self.cursor.execute(sql)  # 执行正确的话就提交事务self.conn.commit()except Exception as e:print(e)self.conn.rollback()  # 回滚,如果出现错误就回滚return itemdef close_spider(self, spider):self.cursor.close()self.conn.close()

9.scrapy中间件的应用

下载中间件
  • 作用:批量拦截请求和响应

  • 拦截请求:

    ​ 1.UA伪装

    ​ 2.代理操作

  • 拦截响应:

    ​ 1.篡改响应数据,篡改不满足需求的响应对象,例如有动态加载的数据,直接请求是请求不到的

    ​ 2.直接更换响应对象

- process_request  request通过下载中间件时,该方法被调用
- process_response 下载结果经过中间件时被此方法处理
- process_exception 下载过程中出现异常时被调用# process_request(request, spider) :当每个request通过下载中间件的时候, 该方法被调用, 该方法必须返回以下三种中的任意一种:1. None: Scrapy将继续处理该request, 执行其他的中间件的响应方法, 知道何时的下载器处理函数(download handler)被调用, 该request被执行(其resposne被下载)2. Response对象: Scrapy将不会调用人其他的process_request()或者process_exception()方法, 或者响应的下载函数; 其将返回response. 已安装的中间件的process_response()方法胡子爱每个res[onse返回时被调用3. Request对象或raise异常: 返回Request对象时: Scrapy停止调用process_request方法并重新调度返回的request. 当新的request被执行之后, 相应的中间件将会根据下载的response被调用.当raises异常时: 安装的下载中间件的process_exception()方法会被调用. 如果没有任何一个方法处理该异常, 则request的errback(Request.errback)方法被调用. 如果没有代码处理抛出的异常, 则该异常被忽略且不被记录.# process_response(request, response, spider) :process_response的返回值也是有三种:1. response对象: 如果返回的是一个Resopnse(可以与传入的response相同, 也可以是全新的对象), 该response会被链中的其他中间件的process_response()方法处理.2. Request对象: 如果其返回一个Request对象, 则中间件链停止, 返回的request会被重新调度下载. 处理类似于process_request()返回request所做的那样.3. raiseu异常: 如果其抛出一个lgnoreRequest异常, 则调用request的errback(Request.errback). 如果没有代码处理抛出的异常, 则该异常被忽略且不记录.process_exception(request, exception, spider) :当下载处理器(downloader handler)或者process_request()(下载中间件)抛出异常(包括lgnoreRequest异常)时, Scrapy调用process_exception().# process_exception()也是返回三者中的一个:1. 返回None: Scrapy将会继续处理该异常, 接着调用已安装的其他中间件的process_exception()方法,知道所有的中间件都被调用完毕, 则调用默认的异常处理.2. 返回Response: 已安装的中间件链的process_response()方法被调用. Scrapy将不会调用任何其他中间件的process_exception()方法.3. 返回一个Request对象: 返回的额request将会被重新调用下载. 浙江停止中间件的process_exception()方法的执行, 就如返回一个response那样. 相当于如果失败了可以在这里进行一次失败的重试, 例如当访问一个网站出现因为频繁爬取被封ip就可以在这里设置增加代理继续访问.
爬虫中间件
案例

爬虫程序

# -*- coding: utf-8 -*-
import scrapy
import requests
from wangyi.items import WangyiItem
from selenium import webdriver
class WySpider(scrapy.Spider):name = 'wy'start_urls = ['https://news.163.com/']un_url = []bro = webdriver.Chrome(executable_path=r'C:\pycahrm文件\chromedriver.exe')def parse(self, response):li_list = response.xpath('//*[@id="index2016_wrap"]/div[1]/div[2]/div[2]/div[2]/div[2]/div/ul/li')model_indexs = [3, 4, 6, 7, 8]for index in model_indexs:li_tag = li_list[index]# 解析出了每一个板块对应的urlmodel_url = li_tag.xpath('./a/@href').extract_first()self.un_url.append(model_url)  # 里面的内容是动态生成的,所以是不满足条件的url,需要在中间件中进行进一步处理yield scrapy.Request(model_url,callback=self.news_parse)def news_parse(self, response):div_list = response.xpath('/html/body/div/div[3]/div[4]/div[1]/div/div/ul/li/div/div')for div in div_list:title = div.xpath('./div/div[1]/h3/a/text()').extract_first()# title = div.xpath('./div/div/div[1]/h3/a/text()').extract_first()news_url = div.xpath('./div/div[1]/h3/a/@href').extract_first()item = WangyiItem()#对得到结果进行去除空格换行item['title'] = title.replace('\n','')item['title'] = title.replace('\t','')item['title'] = title.replace(' ','')item['title'] = title.replace('\t\n','')yield scrapy.Request(news_url,callback=self.detail_parse,meta={'item':item})def detail_parse(self,response):item = response.meta['item']content = response.xpath('//*[@id="endText"]//text()').extract()content = ''.join(content)#对得到结果进行去除空格换行item['content'] = content.replace('\n','')item['content'] = content.replace('\t','')item['content'] = content.replace(' ','')item['content'] = content.replace('\t\n','')item['content'] = content.replace('                    \n','')yield item

下载中间件

from scrapy import signals
from scrapy.http import HtmlResponse
import timeclass WangyiDownloaderMiddleware(object):def process_request(self, request, spider):return Nonedef process_response(self, request, response, spider):if request.url in spider.un_url:spider.bro.get(request.url)time.sleep(3)spider.bro.execute_script('window.scrollTo(0,document.body.scrollHeight)')time.sleep(2)spider.bro.execute_script('window.scrollTo(0,document.body.scrollHeight)')time.sleep(2)page_text = spider.bro.page_source# 新的响应的构造方法new_response = HtmlResponse(url=request.url,body=page_text,encoding='utf-8',request=request)return new_response  #拦截响应后并篡改后,并重新发送新的响应对象,响应的构造方法else:return responsedef process_exception(self, request, exception, spider):pass

10.CrawlSpider(全站数据爬取)

创建一个基于CrawlSpider的爬虫文件:scrapy genspider -t crawl sun www.xxx.com

CrawlSpider使用

爬取阳光热线问政平台所有页码中的内容标题以及状态http://wz.sun0769.com/html/top/report.shtml

阳光问政平台共151711条记录,好几百页,实现这几百页的数据爬取
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor #链接提取器
from scrapy.spiders import CrawlSpider, Rule# rule规则解析器
# 链接提取器:提取链接,可以根据指定的规则进行指定的连链接的提取
#     提取规则:allow='正则表达式'# 规则解析器:获取链接提取到的链接,然后对其进行请求发送,根据指定的规则(callback)对请求的页面源码数据进行数据解析class SunSpider(CrawlSpider):name = 'sun'start_urls = ['http://wz.sun0769.com/index.php/question/report?page=']link = LinkExtractor(allow=r'page=\d+')rules = (# 实例化一个rule对象,基于linkextractorRule(link, callback='parse_item', follow=True),)   # fllow=true  将链接提取器继续作用到链接提取器提取的页码链接所对应 的页面中.如果fllow=Flase,得到的页面数据只是当前页面的显示的页码数据def parse_item(self, response):tr_list = response.xpath('/html//div[8]/table[2]//tr')for tr in tr_list:title = tr.xpath('./td[3]/a[1]/@title').extract_first()status = tr.xpath('./td[4]/span/text()').extract_first()print(title,status)

基于CrawlSpider的深度爬取:爬取所有页码中的详情数据

sun.py

import scrapy
from scrapy.linkextractors import LinkExtractor #链接提取器
from scrapy.spiders import CrawlSpider, Rule# rule规则解析器
from sun_spider.items import SunSpiderItem,SunSpiderItem_secondclass SunSpider(CrawlSpider):name = 'sun'start_urls = ['http://wz.sun0769.com/index.php/question/report?page=']# href="http://wz.sun0769.com/html/question/201912/437515.shtml"link = LinkExtractor(allow=r'page=\d+')link_detail = LinkExtractor(allow=r'question/\d+/\d+/.shtml')rules = (# 实例化一个rule对象,基于linkextractorRule(link, callback='parse_item', follow=True),Rule(link_detail, callback='parse_detail'),)   # fllow=true  将链接提取器继续作用到链接提取器提取的页码链接所对应 的页面中def parse_item(self, response):tr_list = response.xpath('/html//div[8]/table[2]//tr')for tr in tr_list:title = tr.xpath('./td[3]/a[1]/@title').extract_first()status = tr.xpath('./td[4]/span/text()').extract_first()num = tr.xpath('./td[1]/text()').extract_first()item = SunSpiderItem_second()item['title'] = titleitem['status'] = statusitem['num'] = num #用于mysql数据库的条件存储,num就是存储的条件if  num:yield itemdef parse_detail(self,response):content = response.xpath('/html/body/div[9]/table[2]/tbody/tr[1]/td//text()').extract()num = response.xpath('/html/body/div[9]/table[1]/tbody/tr/td[2]/span[2]/text()').extract_first()num = num.split(':')[-1]if num:content = ''.join(content)item = SunSpiderItem()item['content'] = contentitem['num'] = numyield item

items.py

import scrapyclass SunSpiderItem(scrapy.Item):# define the fields for your item here like:content = scrapy.Field()num = scrapy.Field()
class SunSpiderItem_second(scrapy.Item):# define the fields for your item here like:title = scrapy.Field()status = scrapy.Field()num = scrapy.Field()

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.htmlimport pymysql
class SunSpiderPipeline(object):conn = Nonecursor = Nonedef open_spider(self, spider):self.conn = pymysql.Connect(host='127.0.0.1', port=3306, user='root', password='123', db='spider',charset='utf8')def process_item(self, item, spider):if item.__class__.__name__ == 'SunSpiderItem':content = item['content']num = item['num']sql = f'insert into spider_crawl_sun values {(content)} where num={num}'print(num)# sql = 'insert into spider_crawl_sun values ("%s") where num=%s'%(content,num)self.cursor = self.conn.cursor()try:self.cursor.execute(sql)  # 执行正确的话就提交事务self.conn.commit()except Exception as e:self.conn.rollback()return itemelif item.__class__.__name__=='SunSpiderItem_second':title = item['title']status = item['status']num = item['num']sql = 'insert into spider_crawl_sun values ("%s","%s","%s")'%(title,status,num)print(sql)self.cursor = self.conn.cursor()try:self.cursor.execute(sql)  # 执行正确的话就提交事务self.conn.commit()except Exception as e:self.conn.rollback()return itemdef close_spider(self, spider):self.cursor.close()self.conn.close()

11.提升scrapy爬取数据的效率

需要将如下五个步骤配置在配置文件中即可
增加并发:默认scrapy开启的并发线程为32个,可以适当进行增加。在settings配置文件中修改CONCURRENT_REQUESTS = 100值为100,并发设置成了为100。降低日志级别:在运行scrapy时,会有大量日志信息的输出,为了减少CPU的使用率。可以设置log输出信息为INFO或者ERROR即可。在配置文件中编写:LOG_LEVEL = ‘INFO’禁止cookie:如果不是真的需要cookie,则在scrapy爬取数据时可以禁止cookie从而减少CPU的使用率,提升爬取效率。在配置文件中编写:COOKIES_ENABLED = False禁止重试:对失败的HTTP进行重新请求(重试)会减慢爬取速度,因此可以禁止重试。在配置文件中编写:RETRY_ENABLED = False减少下载超时:如果对一个非常慢的链接进行爬取,减少下载超时可以能让卡住的链接快速被放弃,从而提升效率。在配置文件中进行编写:DOWNLOAD_TIMEOUT = 10 超时时间为10s

本文仅做项目练习,切勿商用!

由于文章篇幅有限,文档资料内容较多,需要这些文档的朋友,可以加小助手微信免费获取,【保证100%免费】,中国人不骗中国人。
请添加图片描述
今天就分享到这里,感谢大家收看。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/36289.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GPT-5对普通人有何影响

这篇文章对ChatGPT的使用方法和提问技巧进行了讨论&#xff0c;重点强调了背景信息和具体提问的重要性。文章清晰地传达了如何提高ChatGPT回答的质量&#xff0c;以及个人在使用ChatGPT时的体会和建议。然而&#xff0c;文章在逻辑组织和表达方面还有一些可以改进的地方&#x…

静态库和动态库

1、编译过程 1.预处理&#xff1a;解释并展开源程序当中的所有的预处理指令&#xff0c;此时生成 *.i 文件。 2.编译&#xff1a;词法和语法的分析&#xff0c;生成对应硬件平台的汇编语言文件&#xff0c;此时生成 *.s 文件。 3.汇编&#xff1a;将汇编语言文件翻译为对应处理…

压缩pdf文件大小的方法,如何压缩pdf格式的大小

pdf太大怎么压缩&#xff1f;当你需要通过电子邮件发送一个PDF文件&#xff0c;却发现文件太大无法成功发出时&#xff0c;这些情况下&#xff0c;我们都需要找到一种方法来压缩PDF文件&#xff0c;以便更便捷地进行分享和传输。PDF文件的大小通常与其中包含的图片、图形和文本…

入门JavaWeb之 Response 下载文件

web 服务器接收到客户端的 http 请求 针对这个请求&#xff0c;分别创建一个代表请求的 HttpServletRequest 对象&#xff0c;代表响应的 HttpServletResponse 对象 获取客户端请求过来的参数&#xff1a;HttpServletRequest 给客户端响应一些信息&#xff1a;HttpServletRe…

面试相关-接口测试常问的问题

1.为什么要做接口测试 (1)现在大多系统都是前后端分离的项目,前端和后端的进度可能不一样,那为了尽早的进入测试,前端界面没有开发完成的情况下,只要后端的接口开发完了,就可以提前做接口测试了; (2)基于安全考虑,只依赖前端进行限制,已经完全不满足系统的安全性…

电商卖家怎么快速采集复制1688全店宝贝到自己店铺?淘/猫/拼/抖都适用!

1688上面的货源品类丰富&#xff0c;很多卖家都是在这里找厂家&#xff0c;当我们找好厂家后&#xff0c;怎么将厂家店铺里所有宝贝都复制到自己店铺呢&#xff1f; 虽然1688平台本身支持铺货到其他平台&#xff0c;但一个个铺货太耗费时间了。 阿里巴巴中国站获得1688商品详…

【AI大模型RAG】深入探索检索增强生成(RAG)技术

目录 1. 引言2. RAG技术概述2.1 RAG技术的定义2.2 RAG技术的工作原理2.3 RAG技术的优势2.4 RAG技术的应用场景 3. RAG的工作流程3.1 输入处理3.2 索引建立3.3 信息检索3.4 文档生成3.5 融合与优化 4. RAG范式的演变4.1 初级 RAG 模型4.2 高级 RAG 模型4.3 模块化 RAG 模型优化技…

会计报表分析

目录 一. 会计报表的种类 \quad 一. 会计报表的种类 \quad 反应财务状况的是资产负债表 反应经营成果的是利润表 有时间点的就是静态表 动态表就是有一个区间的, 比如一年, 一个季度等

探索这些有趣的API,让你的应用与众不同

在这个由数据驱动的时代&#xff0c;我们每天都在与各种应用程序和服务互动&#xff0c;却很少意识到它们背后的技术奇迹。API&#xff0c;作为这些互动的幕后英雄&#xff0c;不仅简化了开发过程&#xff0c;还扩展了技术的边界。有趣的API&#xff0c;特别是那些能够激发创新…

HTTP协议和Nginx

一、HTTP协议和Nginx 1.套接字Socket 套接字Socket是进程间通信IPC的一种实现&#xff0c;允许位于不同主机&#xff08;或同一主机&#xff09;上不同进程之间进行通信和数据交换&#xff0c;SocketAPI出现于1983年BSD4.2实现在建立通信连接的每一端&#xff0c;进程间的传输…

理解MySQL核心技术:外键的概念、作用和应用实例

引言 在数据库管理系统&#xff08;DBMS&#xff09;中&#xff0c;外键&#xff08;Foreign Key&#xff09;是维持数据一致性和实现数据完整性的重要工具。本文将详细介绍MySQL外键的基本概念、作用&#xff0c;以及相关的操作指南和应用实例&#xff0c;帮助读者掌握并灵活…

深入了解PHP的If...Else语句

PHP是目前最流行的服务器端编程语言之一&#xff0c;用于开发动态和交互式网站。在PHP编程中&#xff0c;控制结构是非常重要的概念&#xff0c;它们决定了代码的执行流程。其中&#xff0c;if…else语句是最常用的控制结构之一。本文将深入介绍PHP中的if…else语句&#xff0c…

算子级血缘和血缘查询管理

数据链路 血缘关系 应用场景&#xff1a;数据资产&#xff0c;数据开发&#xff0c;数据治理&#xff0c;数据安全等等 &#xff08;绿色箭头上面是数据治理&#xff09; 场景&#xff1a; 数据链路的高效盘点与理解 数仓模型的长效优化机制 风险影响的及时全面分析 重复…

Android Studio无法正确引入包内存在的类

Android Studio 无法识别同一个 package 里的类&#xff0c;显示为红色&#xff0c;但是 compile 没有问题。 重启&#xff0c;rebuild,clean都没有用。 多半是因为 Android Studio 之前发生了错误&#xff0c;某些 setting 出了问题。 解决方法如下&#xff1a; 点击菜单中的…

6月27日-四象限法则

四象限法则&#xff0c;又称为艾森豪威尔矩阵&#xff08;Eisenhower Matrix&#xff09;&#xff0c;是一种时间管理和任务优先级排序的方法。它将任务分为四个象限&#xff0c;帮助个人识别哪些任务最重要&#xff0c;哪些可以推迟或委托&#xff0c;以及哪些可以完全忽略。以…

【等保2.0超详细解读,收藏这一篇就够了!】

网络安全等级保护是指对国家、法人、其他组织、个人的重要信息&#xff0c;对信息的存储、传输、处理等过程进行的保障。分级保护的基本思路是“分级、按标准、结合技术和管理”&#xff0c;用安全保护和监测预警的方法&#xff0c;对潜在的安全风险进行检测和处理&#xff0c;…

Spring中的InitializingBean接口

使用方法 Slf4j Component public class MyBean implements InitializingBean {public MyBean() {log.info("> 构造方法");}Overridepublic void afterPropertiesSet() throws Exception {log.info("> afterPropertiesSet方法");} }Spring中的Bean注…

生命在于学习——Python人工智能原理(2.4.2)

四、Python的程序结构与函数 4.4 函数 函数能将代码划分为若干模块&#xff0c;每一个模块可以相对独立的实现某一个功能&#xff0c;函数有两个主要功能&#xff0c;分别是降低编程难度和实现代码复用&#xff0c;函数是一种功能抽象&#xff0c;复用它可以将一个复杂的大问…

使用函数open()的例子

代码&#xff1a; #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> int main(void) {int fd-1;char filename[]"test.txt";fdopen(filename,O_RDWR);if(-1fd){printf("Open file %s failure!,fd…

PyCharm左侧项目区域出现淡黄色背景如何解决

PyCharm左侧项目区域出现淡黄色背景如何解决 解决方法&#xff1a; 1、打开pycharm 文件 - > Setting-> 项目 -> 项目结构 2、添加内容根 为 你的项目根目录即可恢复