畢設模塊之一:最新版 微博網絡爬蟲(可登錄版)

                                                 前言以及微博站點簡單說明

論文查重和格式檢測完成後,就繼續做畢業設計。不過微博爬蟲真的各種問題調試了一個月,先從破解登錄,分析weibo.com和weibo.cn,發現weibo.com的難度太大,設計很多加密以及時間戳....但是weibo.com上面的信息真的很人性化,不論是佈局還是美觀度,或則我們想要的數據,都是呈現的很清晰明瞭,反觀weibo.cn全是文字,佈局混亂。weibo.cn是以前2g,3g時代用的老版本,在以前時代沒有視頻那些,基本都是文字爲主,圖片也比較少。下面放兩張圖片對比兩個站點。 

                

                                     weibo.cn                                                                                                    weibo.com

 

我一開始就想用weibo.com,用瀏覽器F12和抓包工具charles,嘗試了一段時間,最後放棄了。如果你真的非常想用weibo.com,這裏我推薦一個b站up主破解weibo.com的視頻供大家參考,請點擊:傳送門但是主流我看了博客大多數weibo爬蟲都是用的主流的weibo.cn,可能大家都覺得weibo.com處理太頭疼了,對於博主我來說亦是如此。

小本本記下來:雖然weibo.cn簡陋了一些,但隨之的好處就是登陸難度沒有那麼大(儘管沒那麼大,但是要處理的問題還是很多的)比如數據想用xpath或者json提取,統統都別想,看了以前的文章,好多都在用xpath,json解析數據,我這裏嘗試的時候,發現不管用哪個,解析的返回數據都爲空!最終發現只有把返回的數據轉成文本text,纔能有數據,所以博主這裏採用的scrapy框架+正則提取文本的方式。順便再說下,如果你想用request網頁級提取信息,也很難,博主這裏嘗試了的,scrapy和requests,同樣的hearder,請求的接口,同樣的表單數據,requests是會報錯的,微博不會返回數據給你!這裏博主簡單分析了一下,是因爲在進入weibo.cn域名的時候,會在登陸之前有腳本請求,如果你去分析會發現有個pre_login請求藏在js代碼裏面的,所以如果你很想用requests還得去分析...所以這裏採用scrapy 的原因就是scrapy,在我們請求之前好像默認做了一些瀏覽器事情。

說明:因爲我寫的程序還沒完成,還在進行中,所以只會公佈出目前能夠登陸的源碼和簡單的文章信息提取(未處理數據庫部分),注意:因爲微博這個平臺數據時動態加載的,同時每個新聞信息太長的話,只會顯示一部分,如果需要爬取全部內容需要其提取文章全文的地址,再次請求,才能得到文章全部的信息,下面是展示的動態GIF效果圖。

 

 

不過還是得老生常談,scrapy常用命令熟悉一下~

還是走個流程吧~

 一:開始一個爬蟲項目:scrapy startproject +項目名

 eg:scrapy startproject weibo_info

 會自動生成目錄如下:

 

 

二:創建spider爬蟲

1.創建項目成功需要先切換目錄到weibo_info     

     eg:cd weibo_info

2.創建微博爬蟲    scrapy genspider +爬蟲名 +爬取域名

eg:scrapy genspider weibo weibo.cn 創建完成後,spider目錄下自動生成weibo的spider

 

三:在setting裏面把遵守爬蟲協議改爲不遵守

ROBOTSTXT_OBEY = True 改爲-> ROBOTSTXT_OBEY = False,用時設置下載延遲爲1秒

 

取消DEFAULT_REQUEST_HEADERS 註釋,加入自己的瀏覽器user-agent等信息僞裝瀏覽器。着裏面的信息可以去weibo.cn登錄的時候可以用F12或者抓包工具,看請求頭,然後把對應信息搬過來即可。

登錄的請求頭信息,需要去weibo.cn點擊登錄後,在點擊登錄按鈕,會有個login請求文件,點開它就能看到請求頭和參數,我們就把請求頭信息拿下來,複製到setting裏面的headers裏面。

 

四:打開items.py文件,定義item容器,方便數據的存儲,以及數據庫的信息的儲存。

代碼如下:


class WeiboInfoItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    text_url=scrapy.Field()#新聞url地址
    text_publisher = scrapy.Field()#新聞作者
    text_start_time=scrapy.Field()#新聞發表時間
    text_title=scrapy.Field()#新聞標題
    text_content=scrapy.Field()#新聞內容
    text_like = scrapy.Field()  # 新聞點贊數
    text_transfers=scrapy.Field()#新聞轉發量
    text_comments_numbers=scrapy.Field()#新聞評論數


class WeiboInfo_hot_commentsItem(scrapy.Item):
    hot_comment=scrapy.Field()#熱點評論
    hot_like=scrapy.Field()#點贊數

五:把微博中的spider編寫完成,在spider目錄下找到weibo.py文件,編寫 爬取邏輯代碼

提取方法是正則提取,爲什麼不用xpath和json那麼好用,不用呢?原因前面說了, 這兩種方法得到結果是沒有數據的,需要轉換成text類型纔可以,而文本類型用正則提取是很快很方便的。

這裏附上正學習正則的網址,菜鳥教程:點擊進入正則提取教程 。文章不長可以簡單看下了解正則的常用提取方法。

代碼如下:

這裏要特別說明weibo.py註釋的代碼,是因爲我自己需求變動或者實現起來困難較多,暫時還沒完善,有需要完善的小夥伴自己拿到源碼,可以修改。

# -*- coding: utf-8 -*-
import scrapy,json
from ..items import *
import time,re


class WeiboSpider(scrapy.Spider):
    name = 'weibo'
    allowed_domains = ['weibo.cn','weibo.com']
    start_urls = ['https://weibo.cn']

    #登錄   在爬取信息之前先登錄
    def start_requests(self):
        login_url="https://passport.weibo.cn/sso/login"
        fromdata={
            'username': 'xxxx', #你的賬號
            'password': 'xxx', #你的密碼
            'savestate': '1',
            'r': 'https://weibo.cn/',
            'ec': '0',
            'pagerefer': 'https://weibo.cn/pub/',
            'entry': 'mweibo',
            'wentry': '',
            'loginfrom': '',
            'client_id': '',
            'code': '',
            'qq': '',
            'mainpageflag': '1',
            'hff': '',
            'hfp': '',
        }
        yield scrapy.FormRequest(url=login_url,formdata=fromdata,callback=self.parse_login)

    def parse_login(self,response):
        #判斷是否成功登錄微博
        json_res=json.loads(response.text)
        if json_res['retcode']==20000000:
            info_url="https://weibo.cn/"
            yield  scrapy.Request(url=info_url,callback=self.parse_info)
        else:
            print("*"*100+'\n'+"登錄失敗")



    def parse_info(self, response):
        item= WeiboInfoItem() #導入item容器
        #每一篇文章對應的評論 文章是在數據庫是自增的id從1開始,標記初始也爲1
        text_comment_flag=1
        print("+"*100)
        # print(response.text)
        # 要轉換成tex文本格式,因爲微博用的動態加載數據,用xpath的話,數據提取不到
        res=response.text
        # print(res)
        weibo_info=re.findall(r'<div class="c" id=".*?">(.*?)<div class="s">',res)
        for info in weibo_info:
            # print(info)
            try:
                #因爲文章太多了,隨時又在更新新文章,非熱門文章就過濾一下
                text_like= re.findall(r'<a href=.*?add.*?>贊\[(.*?)\]</a>', info)[0]
                text_comments_numbers= re.findall(r'<a href=.*?comment.*?>評論\[(.*?)\]</a>', info)[0]
                #過濾規則  點贊少於2000,或者評論少於200就pass掉
                # if int(text_like)<2000 or int(text_comments_numbers)<200:
                #     continue

                item['text_url']=re.findall(r'<span class="ctt">.*?<a href="(.*?)".*?',info)[0]#正則提取文章地址
                item['text_publisher']=re.findall(r'<a class="nk" href=.*?>(.*?)</a>',info)[0]
                text_start_time=re.findall(r'<span class="ct">(.*?)&nbsp',info)[0]
                # item['text_start_time']=
                item['text_title']=re.findall(r'【.*?>(.*?)</a>',info)[0]
                item['text_content']=re.findall(r'】(.*?)</span>',info)[0]
                ###新聞內容顯示不全,源出處提取全部新聞內容
                # text_content=re.findall(r'】(.*?)</span>',info)[0]
                # if '>全文<' in text_content:
                #     #提取新聞全文網址
                #     detaile_content_url=re.findall(r"<a href='(.*?)'>全文</a>",text_content)[0]
                #     #再次請求全文網址
                #     print("全文地址:"+self.start_urls[0]+detaile_content_url)
                #
                #     yield scrapy.Request(url=self.start_urls[0]+detaile_content_url,callback=self.parse_detaile_content)

                    #過濾文章內容較短的
                    # if len(detaile_content['text_content'])<200:
                    #     continue
                    # item['text_content'] = detaile_content
                    # print("@"*100)
                    # print(detaile_content)
                # else:
                #                 #     print("else "*50)
                #                 #     item['text_content'] = text_content
                item['text_like']=text_like
                item['text_transfers']=re.findall(r'<a href=.*?repost.*?>轉發\[(.*?)\]</a>',info)[0]
                item['text_comments_numbers']=text_comments_numbers


                print("文章地址:"+item['text_url'])
                print("文章發表人:"+item['text_publisher'])
                print("文章時間:"+text_start_time)
                print("文章標題:"+item['text_title'])
                print("文章內容:"+item['text_content'])
                print("文章點贊:"+item['text_like'])
                print("文章轉發:"+item['text_transfers'])
                print("文章評論:"+item['text_comments_numbers'])
                print("文章內容長度"+str(len(item['text_content'])))


                #**********提取評論*********#
                # comments_url=re.findall(r'轉發\[.*?\]</a>&nbsp;<a href="(.*?)" class="cc">評論.*?</a>',info)[0]
                # print("文章評論url:"+comments_url)
                # yield scrapy.Request(url=comments_url,meta={'flag':text_comment_flag},callback=self.parse_comments)
                # text_comment_flag+=1
                # # 分割線用來分割每個新聞
                print("*" * 100)
            except:
                continue

        # 提取下一頁新聞地址
        try:
            next_url=re.findall(r'<form action="/\?.*?<a href="(.*?)">下頁</a>',res)[0].replace("amp;","")
            print("=" * 100)
        except:
            next_url=None
        # 如果地址存在就拼接域名,再次請求,回調自身再次提取
        if next_url:
            print(self.start_urls[0]+next_url)
            yield scrapy.Request(url=self.start_urls[0]+next_url,callback=self.parse_info)

    #提取評論
    def parse_comments(self,response):
        item=WeiboInfo_hot_commentsItem()#導入熱點評論容器
        print("6"*100)
        flag=response.meta['flag']
        print("flag:"+str(flag))
        res=response.text
        print(res)
        # hot_comments=re.findall(r'<span class="ctt">(.*?)</span>',res)
        # print(hot_comments)


下面應該是本文中的精華~

對於上面的代碼部分解釋和剖析:

def  start_requests(self)是scrapy的一個類的實例,是在爬蟲開始之前做的工作,在提取信息之前,我們需要先登錄賬號的和密碼的。

在weibo.cn中我們點擊登錄後(我這裏先把密碼輸入錯誤),你在瀏覽器F12中或者在抓包工具中,可以在all或者xhr中發現login的ajax請求,會向服務器發送表單數據,裏面包含我們的賬號和密碼,如果輸入錯誤就不會顯示賬號,而輸入正確,則會在很短時間內跳轉,你有可能看不到這個請求了,這時候就需要抓包或者用其他方法把這個正確登錄的login停住,我們就能更好的觀察,不過我這裏輸入錯誤,就是不讓跳轉,方便看請求的url,表單數據,參數等。

這裏我們總結出:

  1. 登錄的url https://passport.weibo.cn/sso/login
  2. 需要提交表單數據
    
                'username': 'xxx',#你的賬號
                'password': 'xxx',#你的密碼
                'savestate': '1',
                'r': 'https://weibo.cn/',
                'ec': '0',
                'pagerefer': 'https://weibo.cn/pub/',
                'entry': 'mweibo',
                'wentry': '',
                'loginfrom': '',
                'client_id': '',
                'code': '',
                'qq': '',
                'mainpageflag': '1',
                'hff': '',
                'hfp': '',
            
  3. 請求登錄用的是post請求不是get,所以要用scrapy中Request(),同時把表達數據傳入進去。
  4. 登錄成功會返回json數據,裏面有20000000,就代表登錄成功,反之登錄失敗

對於上面的代碼代碼還需要注意的事項

  1. 所有爬取的數據只有經過登錄後才能運行
  2. 爬取的信息是自己登錄賬號關注的人發表的信息,比如博主用的這個小號,關注了中國日報,人民日報等等
  3. 數據庫等後續功能,有需要的請自己完善,也可以參考博主專欄裏面的爬蟲專欄,裏面有scrapy爬取數據後存mysql數據的例子
  4. 爬取的時候一定要限速,至少一秒,在setting裏面或者在循環裏面用time.sleep(),友情提示:請重新註冊一個小號,避免大號被封
  5. 因爲目前博主根據自己的需求也還在修改,所以目前代碼可能還有很多問題,如果遇到了其他問題,可能博主也沒法解決,請大家先自己解決,我這裏就是提供基本的思路和基本代碼,保證基本的數據提取。
  6. 如果大家有問題,歡迎留言,我看到後,會盡量幫助大家,如果有知道的大佬,歡迎提供解決方案,不勝感激。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章