Advertisement

爬取京东具体商品页面_京东商品评论爬取实战

阅读量:

先说说为什么写这个小demo吧,说起来还真的算不上“项目”,之前有一个朋友面试,别人出了这么一道机试题,需求大概是这样紫滴:

  • 1.给定任意京东商品链接,将该商品评论信息拿下,存入csv或者数据库
  • 2.要求使用多任务来提高爬虫获取数据的效率
  • 3.代码简洁,规范,添加必要注释
  • 4.可以使用函数式编程,或者面向对象编程 看到上面四个简单的需求,层次高的童鞋可能就看不下去了,因为太简单了,这里本人的目的是给初学爬虫的工程师们。

让我们进入正题吧:

  • step1: 选定任意商品详情url地址
    这里我们以:Apple iPhone 7 Plus (A1661) 128G 黑色 移动联通电信4G手机 为例

  • step2: 按照如下图操作找到评论的url地址:
    6a85761f128b5f6439bddf419324cbae.png

  • step3:分析接口数据可知:
    642b019f29ceab6ddd03e5f8d9bfe75f.png
    bb138ab59952c0b835ee389fcfec6635.png

  • step4: 找到目标url并确定数据后,分析目标url接口:
    1、分析请求方式,由下图可以看出请求是一个get请求
    20ec0a9887fd86f7b8930d0cbe3427db.png

2、分析url地址规律
注意为了确保找到url地址规律需要多对比不同商品下的评论地址,和同一商品不通分类的url地址

同一商品下不同分页url地址如下 https://sclub.jd.com/comment/productPageComments.action?callback=fetchJSON_comment98vv57 &productId=3133827&score=0&sortType=5&page=0&pageSize=10&isShadowSku=0&fold=1

https://sclub.jd.com/comment/productPageComments.action?callback=fetchJSON_comment98vv57 &productId=3133827&score=0&sortType=5&page=1&pageSize=10&isShadowSku=0&rid=0&fold=1

不同商品同一分页url地址如下: https://sclub.jd.com/comment/productPageComments.action?callback=fetchJSON_comment98vv57 &productId=3133827&score=0&sortType=5&page=1&pageSize=10&isShadowSku=0&rid=0&fold=1

https://sclub.jd.com/comment/productPageComments.action?callback=fetchJSON_comment98vv20 &productId=100002069408&score=0&sortType=5&page=1&pageSize=10&isShadowSku=0&rid=0&fold=1

结论:由上面的url可知,同一商品下的不同分页的url只有page值不同,不同商品下的同一分页URL地址,只有productId不同,callback该参数可以省略。

主要获取商品的评论数据如下: 用户头像、用户昵称、星级、内容、产品信息、评论图片地址、评论视频地址、发布时间。

下面开始撸代码:代码思路比较简单

  • step1:导入相关模块
复制代码
 import requests

    
 import re,json
    
 from requests.exceptions import HTTPError,Timeout,RequestException,ProxyError,ConnectTimeout
    
 from concurrent.futures import ThreadPoolExecutor
    
 import csv,pymysql,threading

- step2: 定义一个类JdSpider,完成爬虫主体代码 在__init__初始化方法中添加相关属性

复制代码
 class JdSpider(object):

    
  
    
     def __init__(self,start_url):
    
     """
    
     :param start_url: 商品url地址
    
     """
    
     #设置商品url地址
    
     self.start_url = start_url
    
  
    
     #创建csv文件
    
     self.csv_file = open('jd.csv','a+')
    
     #csv文件头部用户头像headerUrl、用户昵称nickName、星级startLevel、内容content、产品信息productInfo、评论图片地址commentImages、评论视频地址videoUrl、发布时间publishTime。
    
     fileNames = ['headerUrl','nickName','startLevel','content','productInfo','commentImages','videoUrl','publishTime',]
    
     #创建csv文件句柄
    
     self.writer = csv.DictWriter(self.csv_file,fieldnames=fileNames)
    
     #写入csv文件头部
    
     self.writer.writeheader()
    
  
    
     #创建数据库链接
    
     self.mysql_client = pymysql.Connect(
    
         host='127.0.0.1', user='root', password="1314",
    
         database='jd', port=3306,charset='utf8'
    
     )
    
     #创建游标
    
     self.cursor = self.mysql_client.cursor()
    
  
    
     # 实例化一个lock(互斥锁)
    
     self.myLock = threading.Lock()
    
  
    
     self.satrt_request(self.start_url)

- step3: start_request :根据商品url地址发起请求获取响应结果,提取商品的id以及能获取的最大页码数量,拼接商品评论url地址,并将所有请求任务添加进线程池。

复制代码
 def start_request(self,start_url):

    
  
    
     #获取商品的id
    
     # match:根据正则表达式,从字符串中提取符合正则表达式的结果,
    
     # 从起始位置开始匹配,如果不符合正则规则,直接返回None,单次
    
     # 匹配
    
  
    
     # search:根据正则表达式,从字符串中提取符合正则表达式的结果
    
     # 从整个字符串中匹配符合正则规则的子串,有结果直接返回,否则返回
    
     # None,单次匹配
    
     #https://item.jd.com/100000177760.html#comment
    
     prodect_id = re.search('d+',start_url).group()
    
     firstComUrl = "https://sclub.jd.com/comment/productPageComments.action?callback=&productId=%s&score=0&sortType=5&page=0&pageSize=10&isShadowSku=0&fold=1" % prodect_id
    
     response_text = self.send_request(firstComUrl)
    
     if response_text:
    
         # print('获取到了数据',response_text)
    
         json_data = json.loads(response_text)
    
         #获取能请求评论的最大页码数量
    
         maxPage = int(json_data['maxPage'])
    
         print('能够获取的最大页码数量',maxPage)
    
         #将任务添加线程池中
    
         pool = ThreadPoolExecutor(10)
    
         for page in range(maxPage):
    
             comUrl = "https://sclub.jd.com/comment/productPageComments.action?callback=&productId=%s&score=0&sortType=5&page=%s&pageSize=10&isShadowSku=0&fold=1" % (prodect_id,str(page))
    
             result = pool.submit(self.send_request,comUrl)
    
             result.add_done_callback(self.parse_comments)
    
         pool.shutdown()

- step4: send_request该方法根据url地址使用requests发送请求,返回请求的响应结果

复制代码
 def send_request(self,url,headers={'user-agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'}):

    
      try:
    
      response = requests.get(url,headers=headers,timeout=10)
    
  
    
      if response.status_code == 200:
    
          print('请求成功',response.status_code)
    
          return response.text
    
      except (HTTPError,Timeout,RequestException,ProxyError,ConnectTimeout) as err:
    
      print(err)
    
      return None

- step5: parse_comments解析商品评论接口返回的评论数据,注意这里返回的是一个json字符串,需要使用json.loads将json字符串转换为python数据类型然后取值

复制代码
 def parse_comments(self,future):

    
    response_text = future.result()
    
     if response_text:
    
         #解析数据
    
         comments = json.loads(response_text)['comments']
    
  
    
         for comment in comments:
    
             commentInfo = {}
    
             #姓名
    
             commentInfo['nickName'] = comment['nickname']
    
             #内容
    
             commentInfo['content'] = comment['content']
    
             #其他数据依次获取,给你们留点写代码的机会
    
             .......
    
  
    
             #存储数据
    
             self.save_db_to_csv(commentInfo)
    
             self.save_db_to_db(commentInfo)

- step6:将数据保存到csv文件

复制代码
 def save_db_to_csv(self,commentInfo):

    
       #将数据写入csv文件
    
       self.myLock.acquire()
    
       self.writer.writerow(commentInfo)
    
       self.myLock.release()

- step7:将数据保存到数据库

复制代码
 def save_db_to_db(self,commentInfo):

    
    #将数据写入数据库
    
  
    
    sql = """
    
    INSERT INTO jingdong (%s)
    
    VALUES (%s)
    
    """ % (','.join(commentInfo.keys()),','.join(['%s']*len(commentInfo)))
    
    print(sql,list(commentInfo.values()))
    
    #python中线程安全,
    
    # 加锁
    
    print(self.myLock)
    
    self.myLock.acquire()
    
    try:
    
        self.cursor.execute(sql,list(commentInfo.values()))
    
        self.mysql_client.commit()
    
        print('插入数据成功')
    
    except Exception as err:
    
        self.mysql_client.rollback()
    
        print(err)
    
    # 解锁
    
    self.myLock.release()

- step9:运行调用程序:

复制代码
 if __name__ == "__main__":

    
     start_url = 'https://item.jd.com/5089253.html'
    
     jdSpider = JdSpider(start_url)

结果如下图所示:
79372ed873f4dcef73bed46a5369fc69.png

上面代码中只获取了两个字段,其他字段大家可以自行获取了。

全部评论 (0)

还没有任何评论哟~