您当前的位置:首页 > 计算机 > 编程开发 > Python

Python爬虫:爬取京东商品评论(处理json) urllib3+bs4+sqlite3

时间:08-21来源:作者:点击数:

通过观察京东商品页面返回的评论数据是 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条记录,要是想爬大量数据要么分多次爬,要么就使用多线程爬虫。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门