Featured image of post 利用python爬取京东平台评论及图片并进行分析

利用python爬取京东平台评论及图片并进行分析

课程大作业

# 设计报告内容

# 选题背景

在淘宝、京东等网络平台上购物,逐渐成为大众化的购物方式。但假冒伪劣产品在这个摸不着实物的购物平台严重危害着消费者的购物体验,即使我们可以通过七天无理由退货退款来维护我们的合法权益,但是这样浪费大量的人力和财力。

我们希望能够一次性通过网络购买到心怡的商品,其实我们可以在购买商品之前在对应商品店铺下查看以往买家的购物体验和商品评价,通过商品评价判断该商品是否值得购买。

但是事实并非如此,网络购物平台的一些商家为了吸引顾客购买商品,往往会通过刷假评论诱骗买家,同时也有一大批被‘好评返现’吸引的消费者注入大量注水评论。

虽然往往买家都能够筛选出大部分这样的评论,但人工筛查繁琐、复杂。其实一个好的筛选评论、重构评论的程序能够为消费者提供更真实的商品信息。

# 设计依据

1、爬取淘宝评论学习

blog.csdn.net/qq_40946921/article/details/99684483

2、爬取京东图片学习

blog.csdn.net/dexing07/article/details/77938902

3、创建词云学习

《python程序设计基础》第2版——13.6 创建词云

# 设计原创性

1、设计整体原创

2、部分源于1.2设计依据中三项的修改

# 分析、开发、测试过程介绍

# 分析

通过搜集资料分析得知淘宝平台相对于京东平台,其反爬性更强。因此,本次设计从京东平台爬取评论、评论晒图进行分析重构制作云图。

即用户输入京东商品链接,程序分析提取出链中的id号,通过链接拼接生成该商品评论的url,从评论中提取出lastpage(评论最后一页),将0到lastpage页分成两部分分给两个线程进行爬取,爬取获得商品评论和商品晒图。

对爬取得到的评论进行筛选,即:

1、对每条评论进行逐字统计,若该评论中某字符反复不正常出现(多于该评论总数的30%)即判定为注水评论;

(1)如评论:“这个衣服特别好看。。。。。。。。”,用户通过多次输入符号‘。’完成商家下达的某种好评返现任务或平台的评价任务,导致字符‘。’反复出现率高达30%;(由于某字符数量过多,分母过大导致比例大于30%)

(2)如评论:“好看”,用户仅输入两个字符(字符数少于4)作为评论,其参考价值不大,导致其字符‘好’和字符‘看’字反复出现率高达30%;(总字符数量过少,分子过小导致比例大于30%)

2、对于第一次筛选保留下的评论进行第二次筛选。给定出部分常见词作为好评关键字,统计每条评论的好评次数,若一条评论中好评次数高于5次判定为好评过多;

自定义的好评关键字:GoodComment={‘推荐’,‘好用’,‘满意’,‘舒服’,‘喜欢’,‘买它’,‘优惠’,‘很值’,‘赞’,‘精美’,‘回购’,‘漂亮’,‘好看’,‘不错’,‘新款’,‘实惠’,‘速度快’,‘效果好’,‘很舒适’,‘很柔软’,‘很合身’,‘真在用’,‘好’,‘继续买’,‘非常好’,‘很好’,‘质量不错’,‘挺好的’,‘继续购买’,‘特别好’,‘蛮好’,‘一直非常满意’,‘特别好看’}

对绝对好评记两次好评数量,如好评关键字{‘满意’,‘非常满意’}在评论‘非常满意’中出现两次,而在评论‘比较满意’中只出现一次,即对绝对好评给予一定的筛选。

3、若单条评论总数少于30字,要求有效评论的好评数不得高于评论总字符数的20%;若单条评论总数大于30字,要求有效评论的好评数不得高于评论总字符数的10%且其单个字符重复出现率不得高于20%;

(1)如评论:“衣服好看,非常满意,还很舒服,买它”,出现好评次数高达5次,而其字符总数为17,判定为刷假评。(由于好评次数过多,分母过大导致比例大于20%)

(2)如评论:“好看”,评论总字符为2,好评次数为1,判定为假评论,用户为完成商家下达的某种好评返现任务或平台的评价任务,任务式的评论。(总字符数量过少,分子过小导致比例大于20%)

# 开发

1、第一阶段——从淘宝爬取到京东爬取

对于实验前半阶段,一直投入于淘宝评论爬取,发现往往对淘宝评论进行一次大量数据爬取后的第二次近时间爬取都会出现报错现象。在发现因淘宝反爬造成的错误和研究如何绕过淘宝反爬措施上花费了大量的时间,尝试过多种不同的方法。

通过查找资料偶然发现京东爬取相对于淘宝较简单,最终放弃淘宝评论爬取,转向京东评论爬取。

2、第二阶段——从低速爬取到高速爬取

对于京东平台大量评论的爬取,其速度较慢,爬取时间较长。为了提高爬取速度,采用多线程进行爬取。但是通过测试发现,多线程爬取容易触发京东反爬机制。

京东评论页仅显示最近100页,对此进行反复测试后发现开启双线程可以在提升爬取速度且可在爬取大量数据时不触发平台反爬措施。

3、第三阶段——筛选有效评论并使其可视化

对京东平台爬取到商品评论进行筛选,将有效评论存入result列表中,将淘汰评论存入reason列表中,并将其可视化输出到excel文档中,同时对商品晒图按照用户ID分类进行存储,将有效评论和淘汰评论制作成词云图输出,进行对比。

4、第四阶段——完善设计

对设计进行完善,撰写设计报告;

其中文件NEW评论.xlsx和淘汰评论.xlsx为excel文件存储有效评论和淘汰评论,stop_words.txt文件为GitHub中下载的断停词文件,all_word.png和comment_word.png分别为全部评论和有效评论制作成的词云图,photo文件夹为爬取的评论晒图,其中子文件夹的命名为晒图评论买家的id号。

# 测试

1、对京东某商品进行爬取测试,其链接为https://item.jd.com/10902370587.html,对其id提取后拼接的商品评论链接为:https://club.jd.com/comment/productPageComments.action?callback=fetchJSON_comment98&productId=10902370587&score=0&sortType=5&page=0&pageSize=10&isShadowSku=0&fold=1;

img

2、将有效评论存入(NEW评论.xlsx)中,对每条评论的ID号、评论时间、评论内容进行记录;淘汰评论存入(淘汰评论.xlsx)中,对每条评论的ID号、评论时间、评论内容以及淘汰理由进行记录;

img

部分有效评论(NEW评论.xlsx)

img

部分淘汰评论(淘汰评论.xlsx)

img

3、制作云图

对筛选的评论集合和筛选后的评论集合制成云图

所有评论生成云图(all_word.png)

all_word

有效评论生成云图(comment_word.png)

comment_word

4、评论晒图放置在文件夹eliminate.py同文件夹/photos/文件夹中

img

# 自我评价

# 自我评价

1、通过学习网络爬虫爬取购物平台的评论信息和图片,对爬取的评论进行筛选分类处理后存入两个excel文件中(NEW评论.xlsx和淘汰评论.xlsx);

2、对存入的淘汰评论.xlsx文件进行查看,发现其筛选出的评论确实属于注水评论或虚假评论;

3、对爬的取所有评论和筛选评论制作成词云进行对比,发现其通过筛选处理后的词云能更好的反映出商品情况;

4、对于好评关键词都是本人在测试实验时总结的一些词语,但其数量远远不够,通过增加好评关键词的数目能够精准的提升评论筛选效果,但由于时间有限没有做到大量好评关键词;

5、好评关键词大多为鞋服类商品的好评关键字,为彰显设计程序的功能,本人有特意的爬取鞋子类商品,而在其他商品类(如手机类)的筛选效果就显得不佳,其主要原因还是好评关键字词库不够完善;

6、对于筛选机制,本人对其整体的筛选机制还是比较满意,但是也有些许缺陷,本程序设置的好评数与评论字数的比例参数、好评数目参数、单个字符重复比例参数均有待调整,当其达到最优参数时能够更好的保证注水评论和虚假评论被筛选出,而有效评论能够保留;

7、本程序的参数是本人通过反复测试运行总结出的一些参数,但其未达到最优,本人绝对可以通过深度学习使参数达到最优,但其实现复杂,学习的数据集也较难获得。

# 课程总结、对课程的批评和建议

# 课程总结

通过学习python程序设计,本人绝对python相对于之前学习的C语言,其更加简便、灵活,对于很多C语言需要大量代码都难以实现的功能,python能够通过直接调用完成。

通过本次课程设计学会了爬虫、信息处理等专业课程知识,对python在现实中的作用和python能够干什么有一定的了解。

在课设中,个人对爬虫过程中遇到的反爬问题有自己的解决方法,个人通过使用python做出自己的程序让我对本课程更感兴趣,有满满的成就感。

# 对课程的批评和建议

我个人绝对在没有任何提示的情况独立选择课题还是比较难的,对于我而言,在大部分时间中都是在思考需要做什么,而不是怎么做。对于python的初学者而言,虽然老师有给时间让大家自己查资料找出python课设的方向,但由于在找课设方向时所学的理论知识过少,相信大多数同学当时对python能做什么比较迷茫(至少我是这样的),而随着课程的进行,伴随着巨大压力逐渐找到自己课设的方向。

如果在课设中,老师能够给出更多的建议,相信在后续的课设中,大家就不需要花费大量时间寻找自己课设方向,进而节约大量时间去做课程设计,使大家能够更好的完成自己的课设,可能能够提高课设的质量。

# 附件

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# -*- coding:utf-8 -*-
import requests
import urllib
import time
import threading
import json
import pandas
import os
import re 
import jieba
import wordcloud
from PIL import Image


class myThread (threading.Thread):
    def __init__(self, threadID, name, counter):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.counter = counter
    def run(self):
        print ("开始线程:" )
        get_information()
        print ("退出线程:" )


#找出字符串str中最多的字
def max_letter_count(str):
    count_max=0
    for i in str:
        if str.count(i)>count_max:
            max_char=i
            count_max = str.count(i)
    list=[]
    list.append(max_char)
    list.append(count_max)
    return list


def get_information(id,headers,start_page,end_page,result,reason):
    #目前整理的一些好评关键词
    GoodComment={'推荐','好用','满意','舒服','喜欢','买它','优惠','很值','赞','精美','回购','漂亮','好看','不错','新款','实惠','速度快','效果好','很舒适','很柔软','很合身','真好','继续买','非常好','很好','质量不错','挺好的','继续购买','特别好','蛮好','一直在用','非常满意','特别好看'}
    #输出每一页的评论
    for page in range(start_page,end_page):
        print("正在爬第"+str(page)+"页",end='')
        url='https://club.jd.com/comment/productPageComments.action?callback=fetchJSON_comment98&productId='+id+'&score=0&sortType=5&page='+str(page)+'&pageSize=10&isShadowSku=0&fold=1'
        data=requests.get(url).content.decode('utf-8')
        data=data[data.find('{'):data.rfind('}')+1]
        data=json.loads(data)
        #筛选出有用评论
        for num in data['comments']:
            #爬取商品评论图
            print('.',end='')
            m=0
            path='photo/'+str()+str(num['id'])+'/'
            try:
                for image_url in num['images']:
                    if not os.path.exists(path):
                        os.makedirs(path)
                    if '.jpg' in image_url['imgUrl']:
                        urllib.request.urlretrieve('http:'+image_url['imgUrl'],path+str(num['id'])+'-'+str(m)+'.jpg')
                    else:
                        urllib.request.urlretrieve('http:'+image_url['imgUrl'],path+str(num['id'])+'-'+str(m)+'.png')
                    m=m+1
            except:
                pass
            #如果在某一个评论中某个字重复出现率高达30%,判断为买家的垃圾评论,不写入评论中
            if max_letter_count(num['content'])[1]/len(num['content'])>0.3:    #30%保证了评论字数不得低于4个
                reason.append({
                    "ID":str(num['id']),
                    "评论时间":num['creationTime'],
                    "评论内容":num['content'],
                    "理由":max_letter_count(num['content'])[0]+"重复出现率高达30%"
                    })
            else:
                GoodCommentNum=0
                for Comment in GoodComment:               #如果评论中出现好评关键字,记录加1
                    if Comment in num['content']:
                        GoodCommentNum=GoodCommentNum+1
                        if GoodCommentNum>5:              #如果好评数量大于5,判断为虚假评论
                            reason.append({
                            "ID":str(num['id']),
                            "评论时间":num['creationTime'],
                            "评论内容":num['content'],
                            "理由":"好评过多"
                            })
                if len(num['content'])<30:                    #评论字数少于30字
                    if GoodCommentNum<=10 and GoodCommentNum/len(num['content'])<=2/10:    #如果每10字评论有好评关键字小于2,存入评论
                        result.append({
                        "ID":str(num['id']),
                        "评论时间":num['creationTime'],
                        "评论内容":num['content']
                        })
                    else:
                        reason.append({
                        "ID":str(num['id']),
                        "评论时间":num['creationTime'],
                        "评论内容":num['content'],
                        "理由":"好评过多"
                        })
                else:                                         #评论字数大于30字
                    if max_letter_count(num['content'])[1]/len(num['content'])>0.2:
                        reason.append({
                            "ID":str(num['id']),
                            "评论时间":num['creationTime'],
                            "评论内容":num['content'],
                            "理由":max_letter_count(num['content'])[0]+"重复出现率高达20%"
                            })
                    else:
                        if GoodCommentNum<=10 and GoodCommentNum/len(num['content'])<1/10:    #如果每10字评论有好评关键字小于1,存入评论
                            result.append({
                            "ID":str(num['id']),
                            "评论时间":num['creationTime'],
                            "评论内容":num['content']
                            })
                        else:
                            reason.append({
                            "ID":str(num['id']),
                            "评论时间":num['creationTime'],
                            "评论内容":num['content'],
                            "理由":"好评过多"
                            })
        print("\n爬完第"+str(page)+"页")


def cloud_word(comment_word,name):      #生成词云
    stopwords = [line.strip() for line in open('stop_words.txt', encoding='UTF-8').readlines()]    #加载停用词表
    comment_word=comment_word.encode('utf-8')
    comment_word=jieba.cut(comment_word)     #未去掉停用词的分词结果
    comment_word_spite = []
    for word in comment_word:                #去掉停分词
        if word not in stopwords:
            comment_word_spite.append(word)
    comment_word_spite=' '.join(comment_word_spite)
    wc=wordcloud.WordCloud(           #创建wordcloud对象
        r'msyh.ttc',width=500,height=500,
        background_color='white',font_step=2,
        random_state=False,prefer_horizontal=0.9
        )
    t=wc.generate(comment_word_spite)
    t.to_image().save(name+'.png')



def main():
    #浏览器访问伪装
    headers={
             'cookie':'',
             'referer':'https://item.jd.com/',
             'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36 Edg/91.0.864.37',
            }
    url_input=input("请输入京东商品网址:")
    try:
        result=[]    #筛选过后的评论
        reason=[]    #统计每个被筛选淘汰的评论的淘汰理由
        id=''.join(re.findall(r'com/(.*?)\.html',url_input))
        url='https://club.jd.com/comment/productPageComments.action?callback=fetchJSON_comment98&productId='+str(id)+'&score=0&sortType=5&page=0&pageSize=10&isShadowSku=0&fold=1'
        print(url)
        data=requests.get(url).content.decode('utf-8')
        data=data[data.find('{'):data.rfind('}')+1]
        data=json.loads(data)
        lastpage=data['maxPage']
        print('共有'+str(lastpage)+'页评论')
        threads=[]
        threads.append(threading.Thread(target=get_information,args=(id,headers,0,lastpage//2,result,reason)))
        threads.append(threading.Thread(target=get_information,args=(id,headers,lastpage//2,lastpage,result,reason)))
        # 开启新线程
        for t in threads:
            t.start()
        # 等待所有线程完成
        for t in threads:
            t.join()
        print ("退出主线程")
    except:
         print("提取商品网址信息出错,请重新输入京东商品网址:")
         main()
    # 创建新线程,分2个线程(太少了速度慢,太快了容易被封)
    #将筛选后的评论装入文件
    print("有效评论已装入NEW评论.xlsx文件")
    print("淘汰评论已装入淘汰评论.xlsx文件")
    NEW=pandas.DataFrame(result)
    NEW.to_excel('NEW评论.xlsx')
    Eliminate=pandas.DataFrame(reason)
    Eliminate.to_excel('淘汰评论.xlsx')
    comment_word=''
    for _comment in result:
        comment_word+=_comment["评论内容"]
    all_word=comment_word
    cloud_word(comment_word,'comment_word')
    print("comment云图制作完成")
    for _comment in reason:
        all_word+=_comment["评论内容"]
    cloud_word(all_word,'all_word')
    print("all_comment云图制作完成")

if __name__=='__main__':
    main()
Licensed under CC BY-NC-SA 4.0
使用 hugo 构建 🖤 Stack
版权声明:Licensed under CC BY-NC-SA 4.0「署名-非商业性使用-相同方式共享 4.0 国际」