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

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

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中间件爬取京东图书有详细思考过程(涉及较广适合练手)相关推荐

  1. 一个scrapy框架的爬虫(爬取京东图书)

    我们的这个爬虫设计来爬取京东图书(jd.com). scrapy框架相信大家比较了解了.里面有很多复杂的机制,超出本文的范围. 1.爬虫spider tips: 1.xpath的语法比较坑,但是你可以 ...

  2. python爬取京东书籍_一个scrapy框架的爬虫(爬取京东图书)

    我们的这个爬虫设计来爬取京东图书(jd.com). scrapy框架相信大家比较了解了.里面有很多复杂的机制,超出本文的范围. 1.爬虫spider tips: 1.xpath的语法比较坑,但是你可以 ...

  3. 爬虫学习:爬取京东图书

    爬虫学习:scrapy爬取京东图书,详情页url地址对应的响应并不能满足数据提取的需要price字段(即当前url地址对应的响应与element中不一样存在缺失,所以需要构造能够获取价格的请求) # ...

  4. python学爬虫书籍_Python3实战爬虫之爬取京东图书的图文详解

    最近在学习python3,下面这篇文章主要给大家介绍了关于Python3实战爬虫之爬取京东图书图片的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下 ...

  5. python函数大全书籍京东_用python爬取京东图书排行榜并进行数据整合

    经常在京东买书,突发奇想,向看看京东畅销图书有哪些,于是,就有有了下面的代码. python版本:3.5 编辑器:jupyter 首页链接:http://book.jd.com/booktop/0-0 ...

  6. 【selenium爬虫】 selenium自动化爬取京东图书信息

    一.题目要求:搜索京东图书页,自动点击详情页进入,爬取数据信息并且存储到csv中二.解题思路:1.需要下载一个chromedriver.exe.下载地址:ChromeDriver Mirror 注意: ...

  7. python爬京东联盟_python爬虫框架scrapy实战之爬取京东商城进阶篇

    前言 之前的一篇文章已经讲过怎样获取链接,怎样获得参数了,详情请看python爬取京东商城普通篇,本文将详细介绍利用python爬虫框架scrapy如何爬取京东商城,下面话不多说了,来看看详细的介绍吧 ...

  8. python工具箱查询手册书籍京东_用python爬取京东图书排行榜并进行数据整合

    经常在京东买书,突发奇想,向看看京东畅销图书有哪些,于是,就有有了下面的代码. python版本:3.5 编辑器:jupyter 首页链接:http://book.jd.com/booktop/0-0 ...

  9. python爬取京东图书_Python抓取京东图书评论数据

    Python抓取京东图书评论数据 来源:中文源码网    浏览: 次    日期:2018年9月2日 [下载文档:  Python抓取京东图书评论数据.txt ] (友情提示:右键点上行txt文档名- ...

最新文章

  1. 从事仪表专业学c语言有用吗,仪器仪表工程就业方向
  2. css margin居中的问题
  3. 那些不敢生孩子的女人,都在怕什么?
  4. OCP大会 | T-Flex 2.0服务器框架介绍(附PDF)
  5. Hibernate知识问答
  6. 流量管理系统产品选型常见问答(FAQ)
  7. 【Linux】在Linux环境下使用VSCode调试C/C++程序
  8. 微博粉丝平台开发全攻略
  9. Hibernate之分页查询
  10. No package python27 available
  11. 《逐梦旅程:Windows游戏编程之从零开始》
  12. linux监控文件变化的程序,在 Linux 下监控程序修改文件
  13. PGP验证数字签名原理
  14. 使用python获取vCenter默认警报定义
  15. Unity 3D中实现敌人追踪
  16. 干货分享!一份你不知道的武汉Web前端开发学习攻略
  17. Configuration file contains invalid cp936 characters in C:\Users\YWX\pip\pip.ini. 已解决!
  18. php开发之Composer包
  19. uniapp实现小程序预览、保存、转发pdf/word/excel等文件
  20. 汉字转拼音,用户表增加拼音字段,并将汉字姓名对应的拼音赋值给拼音字段

热门文章

  1. Ringtone的两种类型三种形式
  2. 软件开发工作会在35-40岁走入死胡同吗?
  3. git命令从gitlab上克隆代码的时候遇到的问题
  4. 空谈用户体验设计方法论
  5. 浅谈对java编程思想的理解
  6. 修改MySQL 数据库名称
  7. 本地搭建chatgpt
  8. r包安装路径 安装位置 设置
  9. 折戟IPO,Soul遭遇了什么?
  10. unity3d UGUI文字模糊