通过观察京东商品页面返回的评论数据是 JSON 格式的,所以抓取指定评论需要使用 JSON 模块中相应的 API 进行分析,而从搜索页面抓取的商品列表需要分析 HTML 代码,所以使用 bs4。在对数据进行分析整理后,需要将数据保存到 sqlite 数据库中,其他模块还包括 os 和 fake_useragent(获取假的消息头,之前用一个消息头好像被封了,带秀)。
- from urllib3 import *
- import sqlite3
- import json
- import os
- from bs4 import BeautifulSoup
- from fake_useragent import UserAgent
首先我们来到京东随便搜索一个商品,并且进入商品具体页面,检测network
不难发现这个页面获取了评论有关的json数据:
将此url在浏览器中访问后
这并不是纯粹的 json 数据,前面还有 fetchJSON_comment...,以及其他一些不属于 JSON 数据的内容,通过不同的商品面叶可以发现 callback 参数的值基本上都不同,回调函数,不妨去掉试试
哈!果然能正常访问,而且内容中也没有了fetchJSON_啥啥啥的,这又方便了不少
于是我们得到了一个基本格式https://sclub.jd.com/comment/productPageComments.action?productId=***&score=0&sortType=5&page=***&pageSize=10&isShadowSku=0&fold=1,不过要注意的是第一页是0
当然其中还有一些零碎的地方需要转为json类型格式(需要将null,false,true用双引号括起来),可以直接通过字符串替换的方式,然后将json数据转换为字典对象
- def getRateDetail(productId, page):
- url = 'https://sclub.jd.com/comment/productPageComments.action?productId='+ str(productId) + '&score=0&sortType=5&page='+str(page)+'&pageSize=10&isShadowSku=0&fold=1'
- r = http.request('GET', url, headers=head)
- c = r.data.decode('GB18030')
- c = c.replace('null', '"null"')
- c = c.replace('false', '"false"')
- c = c.replace('true', '"true"')
- # 将json数据转换为字典对象
- jdjson=json.loads(c)
- return jdjson
并且在此页上有页数信息
以下为获取每个商品页最大评论页数
- #获取具体商品的最后一页
- def getLastPage(productId):
- jdjson = getRateDetail(productId,1)
- return jdjson['maxPage']
接下来的任务就是抓取搜索页面的所有商品id,商品页网址如https://item.jd.com/5734174.html,不难猜测5734174就是商品id,来到搜素页
可见每个li标签中的data-sku就是商品id,接下来自然就是通过美汤来获取所有商品id的列表
- # 以列表形式返回商品ID
- def getProductIdList():
- url='https://search.jd.com/Search?keyword=cherry&enc=utf-8&wq=cherry&pvid=2db50b439b0747408233915adca72e88'
- r = http.request('GET', url, headers=head)
- #注意如果搜索的内容有中文,网址中就会有utf8的东西,需要ignore忽略utf8一下
- c = r.data.decode('GB18030','ignore').encode('utf8')
- soup = BeautifulSoup(c, 'html.parser')
- liList=soup.find_all('li',attrs={'class':'gl-item'})
- idList = []
- for li in liList:
- idList.append(li['data-sku'])
- #去掉重复的id,不过应该不会重复(利用Set的特性)
- idList = list(set(idList))
- return idList
最后便是加入到sqlite数据库(注意执行完要commit一下)当中了
完整代码:
- from urllib3 import *
- import sqlite3
- import json
- import os
- from bs4 import BeautifulSoup
- from fake_useragent import UserAgent
-
- disable_warnings()
- http = PoolManager()
-
- head = {
- 'User-Agent': str(UserAgent().random)
- }
-
- dbPath = 'jdkb.sqlite'
- if os.path.exists(dbPath):
- os.remove(dbPath)
- conn = sqlite3.connect(dbPath)
- cursor = conn.cursor()
- cursor.execute('''create table kb_comment
- (id integer primary key autoincrement not null,
- content text not null,
- ctime text not null,
- productName text not null);''')
- conn.commit()
-
-
- #获取具体商品的json数据
- def getRateDetail(productId, page):
- url = 'https://sclub.jd.com/comment/productPageComments.action?productId='+ str(productId) + '&score=0&sortType=5&page='+str(page)+'&pageSize=10&isShadowSku=0&fold=1'
- r = http.request('GET', url, headers=head)
- c = r.data.decode('GB18030')
- c = c.replace('null', '"null"')
- c = c.replace('false', '"false"')
- c = c.replace('true', '"true"')
- jdjson=json.loads(c)
- return jdjson
-
-
- #获取具体商品的最后一页
- def getLastPage(productId):
- jdjson = getRateDetail(productId,1)
- return jdjson['maxPage']
-
- # 以列表形式返回商品ID
- def getProductIdList():
- url='https://search.jd.com/Search?keyword=cherry&enc=utf-8&wq=cherry&pvid=2db50b439b0747408233915adca72e88'
- r = http.request('GET', url, headers=head)
- c = r.data.decode('GB18030','ignore').encode('utf8')
- soup = BeautifulSoup(c, 'html.parser')
- liList=soup.find_all('li',attrs={'class':'gl-item'})
- idList = []
- for li in liList:
- idList.append(li['data-sku'])
- #去掉重复的Utl
- idList = list(set(idList))
- return idList
-
-
-
- init = 0
- productIdList=getProductIdList()
- while init < len(productIdList):
- try:
- productId = productIdList[init]
- maxPage = getLastPage(productId)
- page = 0
- while page <= maxPage:
- try:
- jdjs=getRateDetail(productId,page)
- rateList = jdjs['comments']
-
- n=0
- while n<len(rateList):
- content = str(rateList[n]['content'])
- time = str(rateList[n]['creationTime'])
- productName = str(rateList[n]['referenceName'])
- cursor.execute('''insert into kb_comment(content,ctime,productName)
- values('%s','%s','%s')'''%(content,time,productName))
- conn.commit()
- n += 1
- page += 1
- except Exception as e:
- continue
- init+=1
- except Exception as e:
- print(e)
效果如图
···
由于sqlite一次只能 insert 500条记录,要是想爬大量数据要么分多次爬,要么就使用多线程爬虫。