前言以及微博站點簡單說明
論文查重和格式檢測完成後,就繼續做畢業設計。不過微博爬蟲真的各種問題調試了一個月,先從破解登錄,分析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">(.*?) ',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> <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,表單數據,參數等。
這裏我們總結出:
- 登錄的url 是:https://passport.weibo.cn/sso/login
- 需要提交表單數據
'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': '',
- 請求登錄用的是post請求,不是get,所以要用scrapy中Request(),同時把表達數據傳入進去。
- 登錄成功會返回json數據,裏面有20000000,就代表登錄成功,反之登錄失敗
對於上面的代碼代碼還需要注意的事項:
- 所有爬取的數據只有經過登錄後才能運行
- 爬取的信息是自己登錄賬號關注的人發表的信息,比如博主用的這個小號,關注了中國日報,人民日報等等
- 數據庫等後續功能,有需要的請自己完善,也可以參考博主專欄裏面的爬蟲專欄,裏面有scrapy爬取數據後存mysql數據的例子
- 爬取的時候一定要限速,至少一秒,在setting裏面或者在循環裏面用time.sleep(),友情提示:請重新註冊一個小號,避免大號被封。
- 因爲目前博主根據自己的需求也還在修改,所以目前代碼可能還有很多問題,如果遇到了其他問題,可能博主也沒法解決,請大家先自己解決,我這裏就是提供基本的思路和基本代碼,保證基本的數據提取。
- 如果大家有問題,歡迎留言,我看到後,會盡量幫助大家,如果有知道的大佬,歡迎提供解決方案,不勝感激。