scrapy+selenuim中间件爬取京东图书有详细思考过程(涉及较广适合练手)

本文主要是介绍scrapy+selenuim中间件爬取京东图书有详细思考过程(涉及较广适合练手),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

网上很多版本的爬取京东图书都失效了

现在这个版本是能运行的截至到编辑的日期的前后(往后不敢保证)

gitee仓库网址:https://gitee.com/cc2436686/jd_book_spider   (有详细注释和思考过程)

下面就来看看吧

首先看看我们要爬取的页面

   https://book.jd.com/booksort.html

然后用request直接请求在对返回结果进行关键字匹配

好了接下来就转向目标去按f12抓包去

可以看出这个接口就包含了我们想要的内容接下来只要伪造请求获取数据就行了

直接请求这个接口

那就研究研究这个接口

    https://pjapi.jd.com/book/sort?source=bookSort&callback=jsonp_1606557102964_82922

 

经过测试可以的出除了数字以外其他都是固定参数

   而且中间一大串数字一看就是时间戳

  我们可以用python来模拟

import requests
import time#因为要爬取的项目是从这个https://book.jd.com/booksort.html主页面开始的,
# 因为大分类的小说和小分类的中国当代小说包括表示其种类的id都是通过请求接口来渲染
# 所以直接通过请求页面是获取不到相应的数据的所以只能破解下面这个接口
# https://pjapi.jd.com/book/sort?source=bookSort&callback=jsonp_1606487589792_60024
# 直接通过浏览器控制台中的network就能抓到这个包
# 经过测试source后面的参数是不变的
# callback后面的参数是jsonp_加时间戳_随机的四到五位数字
# 下面就用python自带的time软件生成时间戳来进行请求
# 再加上referer参数https://book.jd.com/ 伪装成官网跳转过来就ok了
t=int(time.time()* 1000)url = "https://pjapi.jd.com/book/sort?source=bookSort"
headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36' ,'callback':'jsonp_'+str(t)+'_60005','referer':'https://book.jd.com/'
}
response2 = requests.get(url, headers=headers)
print(response2.request.headers)
print(response2.content.decode())

 

ok接口数据获取成功接下来就去提取页面中的url

 

 

从上面两张图片可以看出分类链接的地址是由大分类小说的fathercategoryid和大分类小说的categoryid和子节点的categoryid拼接而成的

也就是说我们可以直接通过这个接口来构造所有的请求

class BookSpider (scrapy.Spider):name = 'book'# 修改允许的域allowed_domains = ['jd.com']# 修改起始的urlstart_urls = ['https://pjapi.jd.com/book/sort?source=bookSort']# 起始的京东所有图书分类是通过ajax动态加载的所以在页面上搜寻不到所以只能通过# 请求接口来获取所有的图书分类信息和对应的编号用于下面的请求拼接# 此请求接口直接输入接口地址或者去用页面当时加载的页面接口参数去请求是会被认为# 是非法请求如果想要知道如何请求接口请看JD下面的text.py去看相应的伪装过程# 当然也可以去pipelines.py里去看当然没text.py里详细def parse(self, response):# print(response.body.decode())#如果请求成功返回就是json格式用json模块解析big_dict = json.loads (response.body.decode ())for big_node in big_dict['data']:  # 对返回内容中的data进行遍历for small_node in big_node['sonList']:item = JdItem ()item['big_category'] = big_node['categoryName']  # 获取data中大分类的名字赋值给item# 因为大分类的链接点进去之后可以看出是https://channel.jd.com/1713-3258.html格式的可以看出# 是由大分类中的fatherCategoryId: 1713和大分类本身的id拼接而成因为我爬的是图书所以大分类中的# 的fatherCategoryId一直不会变所以图省事直接1713写死了后面再加上大分类本身的id就完成了item['big_category_link'] = 'https://channel.jd.com/1713-' + str (int (big_node['categoryId'])) + '.html'item['small_category'] = small_node['categoryName']# 再找到大分类中的小分类的名字赋值给small_categoryitem['small_category_link'] = "https://list.jd.com/list.html?cat=1713," + str (int (big_node['categoryId'])) + "," + str (int (small_node['categoryId'])) + '&page=1'# 小分类的链接就是大分类的father的id加上小分类的id再加上图书本身的id就行了# 如果不加&page=1拼接之后https://list.jd.com/list.html?cat=1713,3258,3297&page=1请求的默认也是第一页# 因为下面的翻页需要这里还是添加上page=1这个参数# https://list.jd.com/list.html?cat=1713,3258,3297&page=1 小分类的完整链接(样例)# 再为所有的小分类链接创建请求并且跳转到parse_detail这个解析函数再用meta将详细item项目传输过去yield scrapy.Request (url=item['small_category_link'],callback=self.parse_detail,meta={'item': item})

 

构造好所有的小分类的请求url后接下来就是爬取对应的详情页面的图书信息了

我们要爬取的目标

import scrapyclass JdItem(scrapy.Item):#爬虫要抓取的内容如下big_category = scrapy.Field()#图书大分类的名字big_category_link = scrapy.Field()#图书大分类的链接(好像没啥用0.0)small_category = scrapy.Field()#图书小分类的名字small_category_link = scrapy.Field()#图书小分类的链接bookname=scrapy.Field()#图书的名字author=scrapy.Field()#作者的名字(有些图书可能没有作者默认就是None)link =scrapy.Field()#图书的详情页的链接price=scrapy.Field()#图书的价格pass

这些基本的信息提取就不细讲了后面有代码要解决的就是翻页问题因为你查看翻页的按钮信息会发现

又是通过js动态加载的

本着能用scrapy就不用selenuim的原则

继续分析

可以看到前面onclick时间中的参数却透露出信息了

通过翻页前后

第一页

https://list.jd.com/list.html?cat=1713,3258,3297

或者

https://list.jd.com/list.html?cat=1713,3258,3297&page=1

第二页

https://list.jd.com/list.html?cat=1713%2C3258%2C3297&page=3&s=57&click=0

的对比可以发现

page参数是翻页的关键其他代码可以忽略

也就是说只要修改page的信息就可以完成

page是以+=2递增来翻页的

所以我们可以不去提取翻页url(当然也提取不到)直接构造翻页请求

但是如何判断是否是最后一页呢?

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

(我没注意到这个页面图书信息的加载是懒加载

又快马加鞭地修改。。。。。。。。。。。。。。。。。。。。。

好了本人学识浅薄只能用selenuim作为中间件完成了图书的懒加载

中途又遇到频繁请求被重定向的问题导致分支被断

我试了试判断是否被重定向再吃重复发起请求也解决了这个问题

我单独放了一个小分类进去成功完成了一个小分支的所有图书的全部抓取)

好了当无事发生继续分析

当我门翻页后有且只有30个标签也就是说只有30个图书信息但是当我们往下拉的时候

触发了懒加载才能继续加载数据

数量刚好也就是60个所以我们可以使用selenuim作为中间件来触发懒加载

再判断li的个数如果为0就是被重定向了,因为最后一页不可能一本书都没

逻辑如下

        if len (books_list) == 0:print ("貌似被重定向")print("重新发起请求")yield scrapy.Request (url=response.url,callback=self.parse_detail,dont_filter=True,meta={'item': response.meta['item'],'dont_redirect': True})if len (books_list) == 60:# https://list.jd.com/list.html?cat=1713,3258,3297&page=1 #例子url就是这样下面就是对url中的page进行累加操作最后再拼接list = re.split ("&", response.url)# 通过&分隔urlstr1 = str (int (re.findall ('\d+', list[1])[0]) + 2)# 提取第二个page中的数字再将其加2后转为字符串next_url = list[0] + '&page=' + str1# 拼接urlprint ("拼接下一页url", next_url)# 为url创建request请求将meta中的item对象传入# (如果不传入item对象翻页后会因为response取不到item对象而报错而不会去执行下面的代码)yield scrapy.Request (url=next_url,callback=self.parse_detail,dont_filter=True,meta={'item': response.meta['item'],'dont_redirect': True})

因为我是临时加入的selenuim所以只是单纯地作为中间件来使用

大家可能在翻页的时候会遇到

https://list.jd.com/list.html?cat=1713%2C3258%2C3297&page=135&s=4021&click=0

类似的url看到这个cat后面的参数再看看之前cat后面的参数就知道被加密了

百度搜索url解密直接丢进去就能看到

https://list.jd.com/list.html?cat=1713,3258,3297&page=135&s=4021&click=0

跟我们之前id拼接一模一样所以不必担心

直接传我们通过id拼接的url即可

接下来就展示自己的一些关键代码如果想要全部的项目就去gitee仓库那边去拷贝

gitee仓库网址:https://gitee.com/cc2436686/jd_book_spider   (有详细注释和思考过程)

爬虫核心代码book

import timeimport scrapyimport jsonfrom JD.items import JdItemimport reclass BookSpider (scrapy.Spider):name = 'book'# 修改允许的域allowed_domains = ['jd.com']# 修改起始的urlstart_urls = ['https://pjapi.jd.com/book/sort?source=bookSort']# 起始的京东所有图书分类是通过ajax动态加载的所以在页面上搜寻不到所以只能通过# 请求接口来获取所有的图书分类信息和对应的编号用于下面的请求拼接# 此请求接口直接输入接口地址或者去用页面当时加载的页面接口参数去请求是会被认为# 是非法请求如果想要知道如何请求接口请看JD下面的text.py去看相应的伪装过程# 当然也可以去pipelines.py里去看当然没text.py里详细def parse(self, response):# print(response.body.decode())#如果请求成功返回就是json格式用json模块解析big_dict = json.loads (response.body.decode ())for big_node in big_dict['data']:  # 对返回内容中的data进行遍历for small_node in big_node['sonList']:item = JdItem ()item['big_category'] = big_node['categoryName']  # 获取data中大分类的名字赋值给item# 因为大分类的链接点进去之后可以看出是https://channel.jd.com/1713-3258.html格式的可以看出# 是由大分类中的fatherCategoryId: 1713和大分类本身的id拼接而成因为我爬的是图书所以大分类中的# 的fatherCategoryId一直不会变所以图省事直接1713写死了后面再加上大分类本身的id就完成了item['big_category_link'] = 'https://channel.jd.com/1713-' + str (int (big_node['categoryId'])) + '.html'item['small_category'] = small_node['categoryName']# 再找到大分类中的小分类的名字赋值给small_categoryitem['small_category_link'] = "https://list.jd.com/list.html?cat=1713," + str (int (big_node['categoryId'])) + "," + str (int (small_node['categoryId'])) + '&page=1'# 小分类的链接就是大分类的father的id加上小分类的id再加上图书本身的id就行了# 如果不加&page=1拼接之后https://list.jd.com/list.html?cat=1713,3258,3297&page=1请求的默认也是第一页# 因为下面的翻页需要这里还是添加上page=1这个参数# https://list.jd.com/list.html?cat=1713,3258,3297&page=1 小分类的完整链接(样例)# 再为所有的小分类链接创建请求并且跳转到parse_detail这个解析函数再用meta将详细item项目传输过去yield scrapy.Request (# url=item['small_category_link'],url=item['small_category_link'],callback=self.parse_detail,meta={'item': item,'dont_redirect': True})def parse_detail(self, response):# 接收到parse传过来的item信息# 里面包含着大小分类的名字和链接item = response.meta['item']books_list = response.xpath ("//*[@id='J_goodsList']/ul/li")# 查看小分类链接里所有的图书信息都是由ul下面的li标签排列而成的所以就先获取所有的li标签# 通过查看页面后得知翻页是没有对应的url是通过js动态加载的# 但是通过对于翻页前后网址的对比可以看出从翻页操作是通过page+=2完成的# 也就是说只要改变page的值就能实现翻页效果不需要提取url(也没有url可以提取)# 下面就是判断当前页面中的图书也就是目标li的个数是不是60个# 泪崩。。图书是懒加载的得通过selenuim作为中间件渲染页面# 如果是尾页的话就不满30个就会跳过这个判断不去创建请求if len (books_list) == 0:print ("貌似被重定向")time.sleep(3)print("3秒后重新发起请求")yield scrapy.Request (url=response.url,callback=self.parse_detail,dont_filter=True,meta={'item': response.meta['item'],'dont_redirect': True})if len (books_list) == 60:# https://list.jd.com/list.html?cat=1713,3258,3297&page=1 #例子url就是这样下面就是对url中的page进行累加操作最后再拼接list = re.split ("&", response.url)# 通过&分隔urlstr1 = str (int (re.findall ('\d+', list[1])[0]) + 2)# 提取第二个page中的数字再将其加2后转为字符串next_url = list[0] + '&page=' + str1# 拼接urlprint ("拼接下一页url", next_url)# 为url创建request请求将meta中的item对象传入# (如果不传入item对象翻页后会因为response取不到item对象而报错而不会去执行下面的代码)yield scrapy.Request (url=next_url,callback=self.parse_detail,dont_filter=True,meta={'item': response.meta['item'],'dont_redirect': True})for book in books_list:# 下面就是开心的获取信息item['small_category_link'] = response.url# 这里要重置一下小分类链接因为翻页的缘故所以page的信息有所改变item['link'] = response.urljoin (book.xpath ("./div/div[@class='p-img']/a/@href").extract_first ())# 提取到每个图书的详情页链接item['bookname'] = book.xpath ("./div/div[@class='p-name']/a/em/text()").extract_first ()# 提取到每个图书的书名item['author'] = book.xpath ("./div/div[@class='p-bookdetails']/span[@class='p-bi-name']/a/text()").extract_first ()# 提取到每个图书的作者item['price'] = book.xpath ("./div/div[@class='p-price']/strong/i/text()").extract_first ()# 提取到每个图书的价格(跟之前比起来价格不是通过接口请求的而是直接加载到页面的省了一些功夫)yield item# 返回图书的信息

 

下载中间件

from scrapy.http import HtmlResponsefrom selenium import webdriverimport timeclass JdSpiderMiddleware:passclass JdDownloaderMiddleware:# Not all methods need to be defined. If a method is not defined,# scrapy acts as if the downloader middleware does not modify the# passed objects.def __init__(self):opt = webdriver.ChromeOptions ()# opt.add_argument ('--headless')  # 如果想看到具体的翻页情况(就注释)可以打开浏览器看(蛮有趣的)# 建议用有界面的比较好因为可以看到运行情况opt.add_argument ('user-agent="Mozilla/5.0 (iPod; U; CPU iPhone OS 2_1 like Mac OS X; ja-jp) ''AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5F137 Safari/525.20"')opt.add_argument ('https://book.jd.com/booksort.html')self.driver = webdriver.Chrome (options=opt)# 其他都不重要只用看下面这个方法就够了def process_request(self, request, spider):if 'page' in request.url:# 直接跳转小分类链接是会出错的必须要加上referer模拟从https://book.jd.com/booksort.html页面点击过来self.driver.get (request.url)self.driver.execute_script ('window.scrollTo(0,document.body.scrollHeight)')time.sleep (2)html = self.driver.page_sourceself.preurl = request.urlreturn HtmlResponse (url=request.url, body=html.encode ())if "sort?source=bookSort" in request.url:# 判断请求的是不是请求大分类和小分类图书的详细信息的接口网址# 如果是的话就模拟时间戳凭借urlprint ("请求接口数据")t = int (time.time () * 1000)request.headers['callback'] = 'jsonp_{0}_2175'.format (t)# 这里需要添加从https://book.jd.com/的referer信息request.headers['referer'] = 'https://book.jd.com/'return Nonedef close(self, spider):self.driver.quit ()

管道(一定要输入自己的mongodb数据库的ip和端口,还有登陆验证才行)

from pymongo import MongoClientclass JdPipeline:def __init__(self):self.number=0# 创建数据库链接对象#只要创建好mongodb的客户端和登录操作就可以直接插入数据了client = MongoClient('您的mongodb数据库地址',27017)#输入mongodb的ip地址和端口号self.db = client['admin']# 选择admin数据库self.db.authenticate('您的用户名','您的密码')#进行用户验证def process_item(self, item, spider):Item=dict(item)self.db.book.insert(Item)#往book集合里插入数据self.number+=1# print(Item)#书的详细信息print('第',self.number,'本书存入成功')return item

setting(只是配置了下载中间件和管道)

BOT_NAME = 'JD'SPIDER_MODULES = ['JD.spiders']
NEWSPIDER_MODULE = 'JD.spiders'
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'
LOG_LEVEL = "WARNING" 
#如果想看详细信息可以吧LOG_LEVEL改成debug
ROBOTSTXT_OBEY = False
REDIRECT_ENABLED = FalseDOWNLOADER_MIDDLEWARES = {'JD.middlewares.JdDownloaderMiddleware': 543,
}ITEM_PIPELINES = {'JD.pipelines.JdPipeline': 300,
}

items文件

import scrapyclass JdItem(scrapy.Item):#爬虫要抓取的内容如下big_category = scrapy.Field()#图书大分类的名字big_category_link = scrapy.Field()#图书大分类的链接(好像没啥用0.0)small_category = scrapy.Field()#图书小分类的名字small_category_link = scrapy.Field()#图书小分类的链接bookname=scrapy.Field()#图书的名字author=scrapy.Field()#作者的名字(有些图书可能没有作者默认就是None)link =scrapy.Field()#图书的详情页的链接price=scrapy.Field()#图书的价格pass

差不多就这些了再捞一下自己的gitee项目仓库地址

gitee仓库网址:https://gitee.com/cc2436686/jd_book_spider   (有详细注释和思考过程)

效率也就这样吧初步估计有2600万条数据

必须得用分布式爬虫才能爬完

之后会发分布式爬虫版京东图书爬虫

这篇关于scrapy+selenuim中间件爬取京东图书有详细思考过程(涉及较广适合练手)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/902651

相关文章

MySQL数据目录迁移的完整过程

《MySQL数据目录迁移的完整过程》文章详细介绍了将MySQL数据目录迁移到新硬盘的整个过程,包括新硬盘挂载、创建新的数据目录、迁移数据(推荐使用两遍rsync方案)、修改MySQL配置文件和重启验证... 目录1,新硬盘挂载(如果有的话)2,创建新的 mysql 数据目录3,迁移 MySQL 数据(推荐两

MyBatis-Plus逻辑删除实现过程

《MyBatis-Plus逻辑删除实现过程》本文介绍了MyBatis-Plus如何实现逻辑删除功能,包括自动填充字段、配置与实现步骤、常见应用场景,并展示了如何使用remove方法进行逻辑删除,逻辑删... 目录1. 逻辑删除的必要性编程1.1 逻辑删除的定义1.2 逻辑删php除的优点1.3 适用场景2.

SQL Server中行转列方法详细讲解

《SQLServer中行转列方法详细讲解》SQL行转列、列转行可以帮助我们更方便地处理数据,生成需要的报表和结果集,:本文主要介绍SQLServer中行转列方法的相关资料,需要的朋友可以参考下... 目录前言一、为什么需要行转列二、行转列的基本概念三、使用PIVOT运算符进行行转列1.创建示例数据表并插入数

Python + Streamlit项目部署方案超详细教程(非Docker版)

《Python+Streamlit项目部署方案超详细教程(非Docker版)》Streamlit是一款强大的Python框架,专为机器学习及数据可视化打造,:本文主要介绍Python+St... 目录一、针对 Alibaba Cloud linux/Centos 系统的完整部署方案1. 服务器基础配置(阿里

JAVA SpringBoot集成Jasypt进行加密、解密的详细过程

《JAVASpringBoot集成Jasypt进行加密、解密的详细过程》文章详细介绍了如何在SpringBoot项目中集成Jasypt进行加密和解密,包括Jasypt简介、如何添加依赖、配置加密密钥... 目录Java (SpringBoot) 集成 Jasypt 进行加密、解密 - 详细教程一、Jasyp

Java通过ServerSocket与Socket实现通信过程

《Java通过ServerSocket与Socket实现通信过程》本文介绍了Java中的ServerSocket和Socket类,详细讲解了它们的构造方法和使用场景,并通过一个简单的通信示例展示了如何... 目录1 ServerSocket2 Socket3 服务器端4 客户端5 运行结果6 设置超时总结1

Python多任务爬虫实现爬取图片和GDP数据

《Python多任务爬虫实现爬取图片和GDP数据》本文主要介绍了基于FastAPI开发Web站点的方法,包括搭建Web服务器、处理图片资源、实现多任务爬虫和数据可视化,同时,还简要介绍了Python爬... 目录一. 基于FastAPI之Web站点开发1. 基于FastAPI搭建Web服务器2. Web服务

Java 操作 MinIO详细步骤

《Java操作MinIO详细步骤》本文详细介绍了如何使用Java操作MinIO,涵盖了从环境准备、核心API详解到实战场景的全过程,文章从基础的桶和对象操作开始,到大文件分片上传、预签名URL生成... 目录Java 操作 MinIO 全指南:从 API 详解到实战场景引言:为什么选择 MinIO?一、环境

MongoDB搭建过程及单机版部署方法

《MongoDB搭建过程及单机版部署方法》MongoDB是一个灵活、高性能的NoSQL数据库,特别适合快速开发和大规模分布式系统,本文给大家介绍MongoDB搭建过程及单机版部署方法,感兴趣的朋友跟随... 目录前言1️⃣ 核心特点1、文档存储2、无模式(Schema-less)3、高性能4、水平扩展(Sh

Redis的安全机制详细介绍及配置方法

《Redis的安全机制详细介绍及配置方法》本文介绍Redis安全机制的配置方法,包括绑定IP地址、设置密码、保护模式、禁用危险命令、防火墙限制、TLS加密、客户端连接限制、最大内存使用和日志审计等,通... 目录1. 绑定 IP 地址2. 设置密码3. 保护模式4. 禁用危险命令5. 通过防火墙限制访问6.