文章目录

  • 一、任务内容
  • 二、Scrapy安装、配置、调试
  • 三、splash安装、配置、调试
    • 开启hyper-v
    • 安装Docker Desktop
    • 拉取和开启Splash
    • 安装scrapy-splash包
    • 配置scrapy-splash环境
  • 四、Mysql和redis安装、配置、调试
    • 创建数据库
    • 安装pymysql和redis包
  • 四、源代码
    • setting.py
    • items.py
    • hdACT.py
    • pipelines.py
  • 五、运行爬虫

一、任务内容

利用Srapy框架爬取广州图书馆的活动信息,网址为:http://www.gzlib.org.cn/hdActForecast/index.jhtml,该页面是一个活动信息列表,分页显示,每页15条,由JS代码动态生成,点击下一页后页面地址不会发生变化。每一条活动信息都是一个超链接,对应一个唯一的URL,点击后打开一个新的网址,显示该活动信息的详细信息。因此,利用splash作为JS引擎,从而获得渲染后的页面,并模拟点击下一页的动作,获得所有的活动信息。爬取的活动信息保存在Mysql数据库中。
广图会不定期更新活动信息,每隔一天运行一次爬虫程序,但Scrapy会对所有信息重新爬取一遍,包括前一次已经爬取的信息,因此,利用redis内存数据库保存已经爬取的活动信息URL,每次爬取时跳过已经爬取的URL,只对新增的活动信息进行爬取。

二、Scrapy安装、配置、调试

安装Anaconda,在cmd窗口输入:conda install scrapy ,输入y回车表示允许安装依赖库,安装完成后输入scrapy,如果显示如下,则表示安装成功。

在pycharm的工程目录下创建文件夹scrapy,在终端输入指令:

cd scrapy
scrapy startproject gzlib
cd gzlib
scrapy genspider hdACT  www.gzlib.org.cn

scrapy startproject gzlib是创建gzlib项目的命令,scrapy genspider hdACT www.gzlib.org.cn是创建hdACT 爬虫的命令。完成后目录结构如下图所示:

这里需要关注的是hdACT.py文件(爬虫文件),items.py文件(定义需要Pipeline处理的元素),middlewares.py文件(定义spider中间件和downloader中间件),settings.py文件(配置文件)。

三、splash安装、配置、调试

由于爬取的目标网页内容是由JS动态渲染,必须配合JS引擎对网页上的js脚本进行渲染,获得渲染后的页面,这里选择splash作为JS引擎。
安装splash主要的问题是安装Docker,由于我使用的是win10 home版,已经安装了WSL2,并安装了Ubuntu虚拟机(安装方法请自行百度),只需要打开hyper-v虚拟化功能,主要参考了这篇文章

开启hyper-v

把以下的命令保存在一个txt文件中,然后重命名为.cmd文件,最后以管理员身份运行该文件。

pushd '%~dp0'
dir /b %SystemRoot%\servicing\Packages\*Hyper-V*.mum >hyper-v.txt
for /f %%i in ('findstr /i . hyper-v.txt 2^>nul') do dism /online /norestart /add-package:'%SystemRoot%\servicing\Packages\%%i'
del hyper-v.txt
Dism /online /enable-feature /featurename:Microsoft-Hyper-V-All /LimitAccess /ALL

运行过程中会出现百分比,如果运行成功不关闭的话可能会一遍遍运行,当你看到运行成功即可关闭该文件,然后重启电脑就可以拥有完整的Hyper-V选项了。

安装Docker Desktop

从https://www.docker.com/get-docker下载Docker desktop 安装包,直接安装即可。

拉取和开启Splash

在命令行界面输入:

docker pull scrapinghub/splash

拉取splash,完成后,输入:

docker run -p 8050:8050 scrapinghub/splash

完成后,双击桌面上Docker的小鲸鱼图标,启动Docker界面:

点击Open in browser按钮,可在浏览器中看到如下界面,说明安装成功。

安装scrapy-splash包

要在Scrapy中使用splash需要安装scrapy-splash包,在命令行中执行如下命令。

pip install scrapy-splash

配置scrapy-splash环境

在项目配置文件settings.py中,需要配置scrapy-splash,配置内容如下:

#设置Splash服务器地址和端口
SPLASH_URL = 'http://localhost:8050'
# 设置去重过滤器
DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'
# 设置缓存
HTTPCACHE_STORAGE = 'scrapy_splash.SplashAwareFSCacheStorage'
# 开启Splash的两个下载器中间件并调整HttpCompressionMiddleware的次序
DOWNLOAD_MIDDLEWARES = {#将splash middleware 添加到DOWNLOAD_MIDDLEWARE中'scrapy_splash.SplashCookieMiddleware': 723,'scrapy_splash.SplashMiddleware': 725,'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810,
}

四、Mysql和redis安装、配置、调试

从https://dev.mysql.com/downloads/mysql/下载最新版mysql安装程序mysql-installer-community-8.0.28.0.msi,进行安装。
从https://github.com/tporadowski/redis/releases下载最新版的redis安装程序Redis-x64-3.0.504.msi,进行安装。

创建数据库

mysql安装好以后,打开MySql Workbench创建数据库,如下图所示:

redis安装好后,可以自行下载RedisDesktopManager工具,对redis进行管理。如下图:

安装pymysql和redis包

要在python中使用mysql和redis需要相应的包支持,由于我已经安装了anaconda,通过一下命令安装这两个包:

conda install pymysql
conda install redis

四、源代码

setting.py


import scrapy.pipelines.imagesBOT_NAME = 'gzlib'
SPIDER_MODULES = ['gzlib.spiders']
NEWSPIDER_MODULE = 'gzlib.spiders'USER_AGENT = 'Mozilla/5.0(Windows NT6.1;Win64;x64) AppleWebKit/537.36(KHTML,like Gecko) Chrome/78.0.3904.87 Safari/537.36'
ROBOTSTXT_OBEY = False
DOWNLOAD_DELAY = 0.5
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/94.0.4606.71 Safari/537.36'
}
ITEM_PIPELINES = {'gzlib.pipelines.GzlibPipeline': 300,'gzlib.pipelines.GzlibImgPipeline': 1,
}
#图片存储目录
IMAGES_STORE = 'images'
#渲染服务的URL
SPLASH_URL='http://localhost:8050'
#下载器中间件
DOWNLOADER_MIDDLEWARES={'scrapy_splash.SplashCookiesMiddleware':723,'scrapy_splash.SplashMiddleware':725,'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware':810,}
#去重过滤器
DUPEFILTER_CLASS='scrapy_splash.SplashAwareDupeFilter'
#使用Splash的Http缓存
HTTPCACHE_STORAGE='scrapy_splash.SplashAwareFSCacheStorage'

items.py

import scrapy
class GzlibItem(scrapy.Item):name= scrapy.Field()time=scrapy.Field()imgsrc=scrapy.Field()detail_url=scrapy.Field()detail_outline=scrapy.Field()detail_content=scrapy.Field()pass

hdACT.py

import scrapy
from ..items import GzlibItem
import urllib
# 使用scrapy_splash包提供的request对象
from scrapy_splash import SplashRequest
import pymysql
import pandas as pd  # 用来读MySQL
import redis class HdactSpider(scrapy.Spider):name = 'hdACT'allowed_domains = ['www.gzlib.org.cn', 'action.gzlib.org.cn']#列表页和详情页的域名不一样,要把两个域名都加入start_urls = ['http://www.gzlib.org.cn/hdActForecast/index.jhtml']next = False  # 设定是否需要翻页,不翻页的话只爬取列表页的第一个页面redis_db = redis.Redis(host='127.0.0.1', port=6379, db=4)  # 连接redis,相当于MySQL的connredis_data_dict = "f_url"  # 设置redis字典名称connect = pymysql.connect(host='127.0.0.1', port=3306, user='root', password='wjcumt790625',database='scrapy_db', charset='utf8')  # 连接mysql数据库cursor = connect.cursor()  # 建立游标# splash执行”下一页“JS代码的lua脚本next_lua = """function main(splash, args)assert(splash:go(args.url)) splash:wait(10)nxtPage=splash:runjs(args.script)splash:wait(10)return splash:html()end"""def __init__(self):if self.redis_db.hlen(self.redis_data_dict) != 0:#如果redis中有数据self.redis_db.flushdb()  ## 删除redis里面的所有数据sql = "SELECT url FROM gzlibact_tb;"df = pd.read_sql(sql, self.connect)  # 读MySQL中的保存的已经爬取的详情页url数据for url in df['url'].values:  # 把每一条url写入key的字段里self.redis_db.hset(self.redis_data_dict, url, 0)self.connect.close()def start_requests(self):for url in self.start_urls:yield SplashRequest(url,callback=self.parse_splash_actList,args={'wait': 10},  # 最大超时时间endpoint='render.html'  # 使用splash服务的固定参数)def parse_splash_actList(self, response):act_names = response.xpath("//div[@class='yg-detail']/div[@class='yg2-info']/a/h3/text()").extract()act_times = response.xpath("//div[@class='yg-detail']/div[@class='yg2-info']/p/text()").extract()act_detail_urls = response.xpath("//*[@id='actForecast']/li/div/div/a/@href").extract()act_imgsrc=response.xpath("//*[@id='actForecast']/li/div/a/img/@src").extract()for name, time, detail_url,imgsrc in zip(act_names, act_times, act_detail_urls,act_imgsrc):item = GzlibItem()item['name'] = '' if name == None else name.strip()item['time'] = '' if time == None else time.strip()item['detail_url'] = '' if detail_url == None else detail_url.strip()item['imgsrc'] = ['' if imgsrc==None else urllib.parse.urljoin(response.url,imgsrc.strip())]#url拼接if self.redis_db.hexists(self.redis_data_dict,detail_url):  # 取列表中每一条对应的详情页url和key里的字段对比,看是否存在,存在就直接跳过print(detail_url + 'url已经被爬取过。')passelse:print(detail_url + 'url未被爬取过。')self.redis_db.hset(self.redis_data_dict, detail_url, 0)#将新的详情页url作为key保存进redis字典#调用splash获取渲染后的页面yield SplashRequest(detail_url,callback=self.parse_splash_actDetail,meta={'item': item},#将item传递给parse_splash_actDetailargs={'wait': 10},  # 最大超时时间endpoint='render.html'  # 使用splash服务的固定参数)nextJS = response.xpath("//a[contains(text(), '下一页')]/@onclick").extract_first()#‘下一页’对应的JS代码if nextJS and self.next:print('next page script is :' + str(nextJS) + '!!')#调用splash执行下一页对应的JS代码,获取下一页列表yield SplashRequest(response.url,callback=self.parse_splash_actList,args={'wait': 10, 'lua_source': self.next_lua, 'url': response.url, 'script': nextJS},# wait:最大超时时间,lua_source:要执行的lua脚本endpoint='execute'  # 使用splash服务执行JS代码的固定参数)passdef parse_splash_actDetail(self, response):item = response.meta['item']#获取parse_splash_actList传来的itemoutline = response.xpath("// *[ @ id = 'view-text'] / div[@class='action']/ div[@class='title'] /a/text()").extract_first().strip() + '\n'outline += response.xpath("// *[ @ id = 'view-text'] / div[@class='action']/ div[@class='title'] /p")[0].xpath('string()').extract_first().strip() + '\n'for node in response.xpath("// *[ @ id = 'view-text'] / div[@class='p'] "):outline += node.xpath('string(.)').extract_first().strip() + '\n'passitem['detail_outline'] = outlinecontent = ''for node in response.xpath("// div[ @ class = 'view-content'][1] / p "):content += node.xpath('string(.)').extract_first().strip() + '\n'passitem['detail_content'] = content.strip().replace('\n\n','\n')yield itempass

pipelines.py

import hashlib
from scrapy.utils.python import to_bytes
import openpyxl
from scrapy.http import Request
from scrapy.pipelines.images import ImagesPipeline
import pymysql
from itemadapter import ItemAdapterclass GzlibPipeline:connect = pymysql.connect(host='127.0.0.1', port=3306, user='root', password='wjcumt790625',database='scrapy_db', charset='utf8')  # 连接mysql数据库cursor = connect.cursor()  # 建立游标def __init__(self):self.wb = openpyxl.Workbook()self.ws = self.wb.activeself.ws.append(['活动名称', '时间','详情页地址','概要','内容'])def process_item(self, item, spider):line = [item['name'], item['time'],item['detail_url'],item['detail_outline'],item['detail_content']]self.ws.append(line)query = 'insert into gzlibact_tb(name,date,url,outline,content) values(%s, %s, %s, %s, %s)'values=(item['name'], item['time'],item['detail_url'],item['detail_outline'],item['detail_content'])try:self.cursor.execute(query, values)except:self.connect.rollback()return itemdef close_spider(self, spider):self.connect.commit()self.wb.save('hdACT1.xlsx')self.wb.close()class GzlibImgPipeline(ImagesPipeline):def get_media_requests(self, item, info):urls=item['imgsrc']return [Request(u,meta={'item': item}) for u in urls]def file_path(self, request, response=None, info=None, *, item=None):item=request.meta['item']image_name=item['imgsrc'][0].split('/')[-1]return f'full/{image_name}.jpg'

五、运行爬虫

在命令行中输入以下命令:

scrapy crawl hdACT

利用scrapy+splash+redis实现对JS动态生成网页的增量爬取相关推荐

  1. 2)JS动态生成HTML元素的爬取

    2)JS动态生成HTML元素的爬取 import java.util.List;import org.openqa.selenium.By; import org.openqa.selenium.We ...

  2. Python《突破JS动态加载,成功爬取漫画》

    今天就先挑战另外个动态加载的漫画网站,dmzj 漫画太多了,我们先挑选几个漫画下载试一试. 比如我们点击第一部漫画,进去后看看. 这里有很多章节,获得所有的章节这里不难. 接下来我们点击一个章节进去看 ...

  3. java iris_利用K-Means聚类算法实现对iris.data.ulab

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 利用K-Means聚类算法实现对iris.data.ulabel数据的聚类,这是在网上找到如果要换成我的iris.date iris.date.ulabl ...

  4. CV之FDFA:利用MTCNN的脚本实现对LFW数据集进行FD人脸检测和FA人脸校准

    CV之FD&FA:利用MTCNN的脚本实现对LFW数据集进行FD人脸检测和FA人脸校准 目录 运行结果 运行过程 运行(部分)代码 在裁剪好的LFW数据集进行验证 运行结果 运行过程 time ...

  5. Qt实战案例(28)——利用QSQL相关类实现对MySQL数据库的基本操作及相关设置详解

    目录 一.项目介绍 二.项目基本配置 2.1 安装MySQL 2.2 创建Qt项目 2.3 移动libmysql.dll文件 三.UI界面设计 四.主程序实现 4.1 pro文件 4.2 main.c ...

  6. VB.net:VB.net编程语言学习之基于VS软件利用VB.net语言实现对SolidWorks进行二次开发的简介、案例应用之详细攻略

    VB.net:VB.net编程语言学习之基于VS软件利用VB.net语言实现对SolidWorks进行二次开发的简介.案例应用之详细攻略 目录 调用SolidWorks功能简介 1.宏录制步骤 (1) ...

  7. js 动态生成html(js根据后台返回数据生成html页面中的table标签)(转义字符)

    js 动态生成html table标签中内容js生成 html代码 js代码 js 动态生成html 触发事件传参字符转义 table标签中内容js生成 html代码 <div><t ...

  8. JS动态生成的元素,其对应的方法不响应(比如单击事件,鼠标移动事件等)...

    主要原因:在页面给元素注册点击事件的时候[ $(function () {  XXX }); ],JS动态生成的元素还尚未生成,所以click事件就没有生效 解决方法: 方案一:js动态生成元素后再给 ...

  9. node 没有界面的浏览器_node.js爬虫入门(二)爬取动态页面(puppeteer)

    之前第一篇爬虫教程node.js爬虫入门(一)爬取静态页面讲解了静态网页的爬取,十分简单,但是遇到一些动态网页(ajax)的话,直接用之前的方法发送请求就无法获得我们想要的数据.这时就需要通过爬取动态 ...

最新文章

  1. 【数据结构与算法】之深入解析“省份数量”的求解思路与算法示例
  2. JAVA引用和垃圾回收
  3. 【数据结构】普通二叉树的实现
  4. vue是什么软件_Angular vs React vs Vue:2020年的最佳选择是什么?
  5. php 生成图片 打印,php 生成水印图片
  6. Bootstrap列表组
  7. day22,ConfigParser,subprocess,xlrd三个模块
  8. php提取淘宝URL中ID的代码
  9. 马云刘强东隔空互怼,美团外卖大范围故障,苹果系统漏洞百出 | 一周业界事
  10. 在.NET环境中实现每日构建--NAnt篇
  11. MOS管导通条件概述-过程-压降-提高效率等详解
  12. 加了尾注怎么添加新页_如何在毕业论文利用尾注添加参考文献之后再续正文
  13. 问题:控制台报错style-helper.mjs?d002:125 Uncaught (in promise) TypeError: Cannot read properties
  14. 全球与中国硅通孔(TSV)市场深度研究分析报告
  15. vue+ElementUI实现订单页动态添加产品效果
  16. 当梦想照进现实--Steve Nash
  17. win7 win8.1搜索不到隐藏中文wifi 已添加隐藏wifi但是没显示
  18. 带alpha通道的图像合成(Python语言)
  19. Android9.0 Wifi模块Framework层分析
  20. 新品发布 | 极海半导体工业级互联型APM32F107/F105系列MCU,拓展通信外设助力产品应用创新

热门文章

  1. 云顶之奕pbe服务器注册,云顶之弈手游pbe服
  2. codeforces 596E Wilbur and Strings
  3. 解析华为MAS EIE系统硬件与软件结构
  4. 腾创秒会达分布式无线全向麦克风MHD-G3B-13M分体式大功率扬声器
  5. C++ int a= {}和int a = 0
  6. 打雪仗java_【UER #8】打雪仗 - 题目 - Universal Online Judge
  7. 固态硬盘(SSD) 和机 械硬盘(HDD) 优缺点比較
  8. Windows11越过限制安装方法
  9. (转)TensorFlow--实现人脸识别实验精讲 (Face Recognition using Tensorflow)
  10. [XJTUSE 算法设计与分析] 第五章 回溯法