zoukankan      html  css  js  c++  java
  • 微博内容爬取

    在成功获取微博用户的列表之后,我们可以对每个用户的主页内容进行爬取了

    环境

    tools

    1、chrome及其developer tools

    2、python3.6

    3、pycharm

    Python3.6中使用的库

     1 import urllib.error
     2 import urllib.request
     3 import urllib.parse
     4 import urllib
     5 import json
     6 import pandas as pd
     7 import time
     8 import random
     9 import re
    10 from datetime import datetime
    11 from lxml import etree

    爬取字段确定

    首先,我们只管的浏览用户主页,点击全部微博,观察我们能获取到的信息:

    • 用户id
    • 微博id
    • 微博时间
    • 微博内容
    • 微博发布平台
    • 微博评论数
    • 微博点赞数
    • 微博转发数
    • 原微博id
    • 原微博用户id
    • 原微博用户名
    • 原微博内容
    • 原微博评论数
    • 原微博点赞数
    • 原微博转发数

    然后,我们利用Chrome的developer tools观察用户个人主页所能获取到的主要内容,发现有些转发内容如果过长,无法直接通过用户主页进行爬取,而需要点进该条微博链接,对原微博进行爬取。

    因此,我们可以爬取原微博的url,通过解析原微博url的内容来获取原微博的具体内容。

    最终,通过综合情况,最后确定的字段为:

    • 用户id——uid
    • 微博id——mid
    • 微博时间——time
    • 微博发布平台——app_source
    • 微博内容——content
    • 微博评论数、点赞数、转发数——others
    • 微博地址——url
    • 是否转发——is_repost
    • 原微博id——rootmid
    • 原微博用户id——rootuid
    • 原微博名——rootname
    • 原微博地址——rooturl

    加载页包抓取

    在对用户的微博内容进行爬取时,最为困难的是解决网页加载的问题。微博需要两次加载,才能载入微博的全部内容,并进入下一页,因此,如何抓取到加载页的包是我们工作中最为重要的部分。

    这里,我们需要借助Chrome的开发者工具,抓取页面加载时出现的包

    发现加载的时间段中,出现了一个xhr类型的文件,长得最像我们需要的加载包:

    https://weibo.com/p/aj/v6/mblog/mbloglist?ajwvr=6&domain=100505&profile_ftype=1&is_all=1&pagebar=0&pl_name=Pl_Official_MyProfileFeed__22&id=1005051956890840&script_uri=/p/1005051956890840/home&feed_type=0&page=1&pre_page=1&domain_op=100505&__rnd=1517384223025

    再加载一次试验一下,发现出现它又出现了:

    https://weibo.com/p/aj/v6/mblog/mbloglist?ajwvr=6&domain=100505&profile_ftype=1&is_all=1&pagebar=1&pl_name=Pl_Official_MyProfileFeed__22&id=1005051956890840&script_uri=/p/1005051956890840/home&feed_type=0&page=1&pre_page=1&domain_op=100505&__rnd=1517384677638

    看到pl_name=Pl_Official_MyProfileFeed__22基本上就准了,为了保险起见,我们再点开链接看看,然后发现——果然是熟悉的配方,熟悉的味道~~

    仔细解析这段url,发现:

    • is_all是页面属性,表示全部微博
    • page和pre_page都表示页数
    • id是用户id【uid】和domain【100505】的结合体
    • script_uri是当前用户的主页url字段
    • pagebar长得最像加载页,第一个加载页为0,第二个加载页为1
    • __rnd是时间戳,可以省略

    结合初始网页没有pre_page和pagebar这两个字段,我们去掉这两个字段,运行一下url,观察一下所得到的内容,发现为加载前的用户发布的微博内容。

    因此我们可以将用户主页的每一页分为三个部分,分成三个url进行解析,获取整个页面的内容。

    具体代码如下:

     1 # 初始化url
     2 def getBeginURL(self):
     3     begin_url = 'https://weibo.com/p/aj/v6/mblog/mbloglist?ajwvr=6&domain=100505&is_all=1&id=100505'+str(self.uid)+
     4                 '&script_uri=/u/1956890840&domain_op=100505&page='
     5     return begin_url
     6 
     7 # 设置加载页url,并获取html内容
     8 def getHTML(self,page_num,extend_part = ''):
     9     # extend_part为获取加载页的扩展字段
    10     url = self.getBeginURL()+str(page_num)+extend_part
    11     data = urllib.request.urlopen(url).read().decode('utf-8')
    12     html = json.loads(data)['data']
    13     return html
    14 
    15 for x in range(3):
    16     if x == 0:  # 初始页面
    17         extend_part = ''
    18     elif x == 1: 
    19         b = x - 1
    20         extend_part = '&pre_page=' + str(i) + '&pagebar=' + str(b)
    21     elif x == 2:
    22         b = x - 1
    23         extend_part = '&pre_page=' + str(i) + '&pagebar=' + str(b)
    24     html = self.getHTML(i, extend_part)
    25     page = etree.HTML(html)

    以上,最为头大的问题就解决啦~

    博主写代码的时候为了解决加载包的问题头疼了好几天,结果发现Chrome的开发者工具比我想的还要强大的多,不由的感叹自己的愚蠢。发现了加载包的规律有,后面的一切都水到渠成,迅速的完成了微博内容爬取的代码~

    下面是我的代码,各位可以参考。

    博主为了可以实时更新内容,设置了爬取微博的时间段,可以避免每次都爬取页数而造成微博重复爬取的麻烦。

    代码还有很多需要改进的地方,希望各位多多交流~

      1 import urllib.error
      2 import urllib.request
      3 import urllib.parse
      4 import urllib
      5 import json
      6 import pandas as pd
      7 import time
      8 import random
      9 import re
     10 from datetime import datetime
     11 from datetime import timedelta
     12 from lxml import etree
     13 
     14 class getWeiboContent():
     15     """
     16     微博内容爬取:
     17     mid
     18     time
     19     app_source
     20     content
     21     url
     22     others(repost, like, comment)
     23     is_repost
     24     rootmid
     25     rootname
     26     rootuid
     27     rooturl
     28     """
     29     def __init__(self, uid, begin_date=None, begin_page=1, interval=None, flag=True):
     30         self.uid = uid  # 微博用户ID
     31         self.begin_page = begin_page  # 起始页
     32         self.interval = interval  # 需要爬取的页数,默认为None
     33         self.begin_date = begin_date  # 爬取的微博的起始发布日期,默认为None
     34         self.flag = flag
     35 
     36     # 初始化url
     37     def getBeginURL(self):
     38         begin_url = 'https://weibo.com/p/aj/v6/mblog/mbloglist?ajwvr=6&domain=100505&is_all=1&id=100505'+str(self.uid)+
     39                     '&script_uri=/u/1956890840&domain_op=100505&page='
     40         return begin_url
     41 
     42     # 设置加载页url,并获取html内容
     43     def getHTML(self,page_num,extend_part = ''):
     44         url = self.getBeginURL()+str(page_num)+extend_part
     45         print(url)
     46         data = urllib.request.urlopen(url).read().decode('utf-8')
     47         html = json.loads(data)['data']
     48         return html
     49 
     50     # 爬取每条微博的内容,输出字典
     51     def getContent(self,node):
     52         dic = {}
     53         dic['mid'] = node.xpath('./@mid')[0]
     54         print('mid:'+dic['mid'])
     55         dic['time'] = node.xpath('.//div[@class="WB_from S_txt2"]/a[1]/@title')[0]
     56         app_source = node.xpath('.//div[@class="WB_from S_txt2"]/a[2]/text()')
     57         if len(app_source) !=0 :  # 部分微博不显示客户端信息
     58             dic['app_source'] = app_source[0]
     59         content = node.xpath('./*/*/div[@class="WB_text W_f14"]')[0].xpath('string(.)')
     60         dic['content'] = re.compile('
    s*(.*)').findall(content)[0]
     61         others = node.xpath('.//ul[@class="WB_row_line WB_row_r4 clearfix S_line2"]//span[@class="line S_line1"]/span/em[2]/text()')
     62         dic['repost_num'] = others[1]
     63         dic['comment_num'] = others[2]
     64         dic['like_num'] = others[3]
     65         detail_info = node.xpath('./div[@class="WB_feed_handle"]/div/ul/li[2]/a/@action-data')[0]
     66         dic['url'] = re.compile('&url=(.*?)&').findall(detail_info)[0]
     67         rootmid = node.xpath('./@omid')
     68         # 判断是否存在转发微博
     69         if len(rootmid) != 0:
     70             dic['is_repost'] = 1
     71             dic['rootmid'] = rootmid[0]
     72             weibo_expend = node.xpath('./*/*/div[@class="WB_feed_expand"]')[0]
     73             rootname = weibo_expend.xpath('./*/*/a[@class="W_fb S_txt1"]/@nick-name')
     74             # 判断原博是否被删除
     75             if len(rootname) != 0:
     76                 dic['rootuid'] = re.compile('rootuid=(.*?)&').findall(detail_info)[0]
     77                 dic['rootname'] = re.compile('rootname=(.*?)&').findall(detail_info)[0]
     78                 dic['rooturl'] = re.compile('rooturl=(.*?)&').findall(detail_info)[0]
     79 
     80         return dic
     81 
     82     # 获取微博内容
     83     def getWeiboInfo(self):
     84         i = self.begin_page
     85         # 判断是否划定了爬取页数
     86         if self.interval is None:
     87             # 若未划定爬取页数,则设置自动翻页参数hasMore=True
     88             hasMore = True
     89             end_page = False
     90         else:
     91             # 若划定爬取页数,则爬取页数优先
     92             end_page = self.begin_page+self.interval
     93             hasMore = False
     94         # 初始化一个DataFrame用于存储数据
     95         weibo_df = pd.DataFrame()
     96         while (i <= end_page | hasMore) and self.flag:
     97             for x in range(3):
     98                 if x == 0:  # 初始页面
     99                     extend_part = ''
    100                 elif x == 1:  # 第一个加载页
    101                     b = x-1
    102                     extend_part = '&pre_page=' + str(i) + '&pagebar=' + str(b)
    103                 elif x == 2:  # 第二个加载页
    104                     b = x-1
    105                     extend_part = '&pre_page=' + str(i) + '&pagebar=' + str(b)
    106                 html = self.getHTML(i, extend_part)
    107                 page = etree.HTML(html)
    108                 if page is None:
    109                     break
    110                 else:
    111                     detail = page.xpath('//div[@class="WB_cardwrap WB_feed_type S_bg2 WB_feed_like "]')
    112                 # 判断用户是否发过微博
    113                 if len(detail) == 0:
    114                     print('该用户并未发过微博')
    115                     break
    116                 weibo = {}
    117                 weibo['mid'] = []
    118                 weibo['time'] = []
    119                 weibo['content'] = []
    120                 weibo['app_source'] = []
    121                 weibo['url'] = []
    122                 weibo['repost_num'] = []
    123                 weibo['comment_num'] = []
    124                 weibo['like_num'] = []
    125                 weibo['is_repost'] = []
    126                 weibo['rootmid'] = []
    127                 weibo['rootname'] = []
    128                 weibo['rootuid'] = []
    129                 weibo['rooturl'] = []
    130                 for w in detail:
    131                     all_info = self.getContent(w)
    132                     # 判断是否设置了微博的开始日期
    133                     if self.begin_date is None:
    134                         pass
    135                     else:
    136                         weibo_dt = datetime.strptime(all_info['time'], '%Y-%m-%d %H:%M').date()
    137                         begin_dt = datetime.strptime(self.begin_date, "%Y-%m-%d").date()
    138                         # 判断微博发布日期是否在开始日期之后
    139                         if begin_dt > weibo_dt:
    140                             # 当微博发布日期在开始日期之后时,停止爬取
    141                             self.flag = False
    142                             break
    143                     weibo['mid'].append(all_info.get('mid', ''))
    144                     weibo['time'].append(all_info.get('time', ''))
    145                     weibo['app_source'].append(all_info.get('app_source',''))
    146                     weibo['content'].append(all_info.get('content', ''))
    147                     weibo['url'].append(all_info.get('url', ''))
    148                     weibo['repost_num'].append(all_info.get('repost_num', ''))
    149                     weibo['comment_num'].append(all_info.get('comment_num', ''))
    150                     weibo['like_num'].append(all_info.get('like_num', ''))
    151                     weibo['is_repost'].append(all_info.get('is_repost', 0))
    152                     weibo['rootmid'].append(all_info.get('rootmid', ''))
    153                     weibo['rootname'].append(all_info.get('rootname', ''))
    154                     weibo['rootuid'].append(all_info.get('rootuid', ''))
    155                     weibo['rooturl'].append(all_info.get('rooturl', ''))
    156                 weibo = pd.DataFrame(weibo)
    157                 weibo['uid'] = self.uid
    158                 weibo_df = weibo_df.append(weibo,ignore_index=True)
    159             # 提取下一页链接
    160             if page is None:
    161                 break
    162             else:
    163                 next_page = page.xpath('//a[@class="page next S_txt1 S_line1"]/@href')
    164             if len(next_page) == 0:  # 判断是否存在下一页
    165                 self.flag = False
    166                 print('已是最后一页')
    167             else:
    168                 page_num = re.compile('page=(d*)').findall(next_page[0])[0]
    169                 i = int(page_num)
    170             time.sleep(random.randint(5, 10))  # 设置睡眠时间
    171         return weibo_df
    172 
    173 if __name__=='__main__':
    174     uid = input('请输入uid:')
    175     begin_date = input('请输入日期,格式为xxxx-xx-xx:')
    176     begin_page = input('请输入开始页,默认为1:')
    177     getWeiboContent(uid, begin_date).getWeiboInfo()
    View Code
  • 相关阅读:
    C# NameValueCollection
    visual studio使用技巧创建自己代码片段
    C#在DataTable中使用LINQ
    [转]C# 中的.pdb/ .vshost.exe/ .vshost.exe.manifest文件讨论
    C#自定义控件在添加引用后不显示在工具箱的解决方法
    Java 工程师
    redis-CRC16
    sql server-当天日期减去一天 应该如何写
    清除访问Windows共享时缓存的凭据
    cmd下查看当前登陆用户
  • 原文地址:https://www.cnblogs.com/xmyzero/p/8392943.html
Copyright © 2011-2022 走看看