Python爬取百度貼吧回帖中的微訊號(基於簡單http請求)
作者:草小誠
轉載請注原文地址:https://blog.csdn.net/cxcjoker7894/article/details/85685115
前些日子媳婦兒有個需求,想要一個任意貼吧近期主題帖的所有回帖中的微訊號,用來做一些微商的操作,你懂的。因為有些貼吧專門就是微商互加,或者客戶留微信的,還有專門特定使用者群的貼吧,非常精準,我們一致認為比其他加人模式效率要高,所以如果能方便快捷的提取微訊號,價值還是很高的(事後來看微訊號到購買轉化率約1%,已經很滿意了)。
那需求很明確,說幹就幹,當天晚上就想把這個工具實現出來,我呢開啟電腦開始調研 BeautifulSoup 和 Scrapy,媳婦兒開始整理目標貼吧,考慮關鍵詞的過濾和回帖正則的匹配方式。在她一步步點選操作中我就發現,百度貼吧的 url 和 css 名極為規整,分頁與功能按鈕也都簡單的在 GET 請求的 url 中體現,而且沒有混淆或加密等等。既然如此,還用得著爬蟲框架麼,直接迴圈 http 請求,想辦法從返回的 HTML 中提取微訊號就行了,非常簡單。
下面都以“寶媽微信群”吧為例:
1.訪問貼吧,發現 url 如下:
https://tieba.baidu.com/f?kw=寶媽微信群ie=utf-8
對比其他貼吧,格式一致,地址/f,引數 kw(keyword) 和 ie,kw即為貼吧名稱,ie是編碼不用動。由於需求只要近期的帖子,所以這裡不翻頁。
2.訪問主題帖,發現 url 如下:
https://tieba.baidu.com/p/5981196309
對比其他帖子,真的是太規整了,/p/ 就是帖子,後面跟帖子id即可,帖子id唯一且不會變化。
在主題帖列表的html中,包含 ‘j_th_tit’ 屬性 並 不含 ‘threadlist_title’ 屬性的元素即為主題帖連結。
3.分頁怎麼辦?點一下第五頁:
https://tieba.baidu.com/p/5144424400?pn=5
bing~加一個 pn 引數就可以訪問第N頁,這裡有個問題是,如果pn值超過當前主題帖最大頁數,就會仍然顯示本貼的最後一頁,也就是說如果只有5頁,即使傳100也還是顯示第5頁,所以迴圈頁數的時候需要根據屏顯的頁數判斷是否翻頁了,如果沒有真的翻頁就continue。
至此,所有的 HTML 都可以獲取到了,要想辦法提取其中的回帖資訊,分為三部分:
- 按行讀,並切割拆分元素,依靠標籤名和class名,提取有用的html元素。
- 排除噪音干擾,去掉一些夾雜著的的 js 程式碼段。
- 將有用的html元素分別處理,微信去提取,分頁去判斷,等等。
舉例,class="tP"的元素是分頁顯示,含有‘d_post_content j_d_post_content’ 的元素是回覆,由於HTML內容龐大,就不在文中貼出了。
由於沒有想出好的微訊號判斷邏輯,就簡單的將不包含英文和數字的回覆過濾掉了。
再將 while true 迴圈翻頁的邏輯寫好,就大功告成了,把爬下來的有效回覆全都寫到檔案裡就好,一個貼吧首頁所有主題帖中的所有有效回覆,就提取出來了。
貼一下成果圖,這個吧大約有2000條:
下面貼程式碼吧,看官可以拿去作為工具使用,前三個變數分別為:
- tieba_name : 貼吧名稱,注意不含“吧”字。
- tiezi_names : 這裡可以限定帖子的名稱,如果設定了,會只爬取包含有設定的關鍵詞的帖子。
- store_file_path : 結果儲存在哪裡?檔案路徑。
其他的無需改動,即可使用:
#coding=utf-8
'''
@author: caoxiaocheng
'''
import requests
import re
import string
import time
tieba_name = '寶媽微信群'
tiezi_names = []
store_file_path = 'C:\\Users\\user\\Desktop\\貼吧爬取資料%s.txt' % str(int(time.time()))
def special_print(print_str, store_file=None):
print('★★★★★★★★ ' + str(print_str), flush=True)
if store_file:
store_file.write('★★★★★★★★ ' + str(print_str)+'\n')
def tiezi_check(tiezi_names, tieba_title_line):
if not tiezi_names:
return True
for tiezi_name in tiezi_names:
if tiezi_name in tieba_title_line:
return True
return False
def word_check(word, check_words):
for check_word in check_words:
if check_word in word:
return False
if re.match('..\:..', word):
return False
num_or_letter = False
for one_char in word:
if one_char in [str(x) for x in string.digits] or one_char in [str(x) for x in string.ascii_letters]:
num_or_letter = True
break
if not num_or_letter:
return False
return True
def get_resp_line(resp):
resp_html = resp.content.decode('utf-8')
resp_lines = resp_html.split('\n')
return resp_lines
tieba_index_url = 'https://tieba.baidu.com/f'
tieba_detail_url = 'https://tieba.baidu.com'
tieba_index_params = {'kw': tieba_name,
'ie': 'utf-8',
}
detail_link_mapping = {}
check_words = ['該樓層疑似違規已被系統摺疊',
'送TA禮物',
'收起回覆',
'function']
# 將目標貼吧首頁的所有主題帖連結存下來
special_print('正在獲取帖子列表')
tieba_index_resp = requests.get(tieba_index_url, tieba_index_params)
tieba_index_lines = get_resp_line(tieba_index_resp)
for tieba_index_line in tieba_index_lines:
if 'j_th_tit' in tieba_index_line and 'threadlist_title' not in tieba_index_line and tiezi_check(tiezi_names, tieba_index_line):
title_line = tieba_index_line.strip()
title_name = title_line.split(' ')[3][7:-1]
title_link_tail = title_line.split(' ')[2][6:-1]
title_link = tieba_detail_url + title_link_tail
detail_link_mapping[title_link] = title_name
for detail_link in detail_link_mapping:
print('標題:'+detail_link_mapping[detail_link])
print('連結:'+detail_link)
# 遍歷所有主題帖,爬取主題帖所有回覆
with open(store_file_path, 'a', encoding='utf-8') as store_file:
for detail_link in detail_link_mapping:
title_name = detail_link_mapping[detail_link]
special_print('正在爬取帖子:'+title_name, store_file)
last_page = 0
current_page = 1
try:
while(True):
special_print('正在爬取第'+str(last_page+1)+'頁', store_file)
detail_link_resp = requests.get(detail_link+'?pn='+str(last_page+1))
detail_link_lines = get_resp_line(detail_link_resp)
more_page = False
for detail_link_line in detail_link_lines:
detail_line = detail_link_line.strip()
detail_line_fix = re.sub(u"\\<.*?\\>", "", detail_line)
if 'class="tP"' in detail_link_line:
more_page = True
current_page = int(detail_line_fix.strip())
if current_page == last_page:
special_print('沒有更多頁了', store_file)
raise IndexError
if 'd_post_content j_d_post_content' in detail_link_line and 'topic_name' not in detail_link_line:
if 'void' in detail_line_fix:
detail_line_fix = detail_line_fix[:detail_line_fix.index('void')]
detail_line_words = [x for x in detail_line_fix.split(' ') if x]
for detail_line_word in detail_line_words:
if word_check(detail_line_word, check_words):
print(detail_line_word)
store_file.write(detail_line_word+'\n')
if not more_page:
special_print('沒有更多頁了', store_file)
raise IndexError
last_page = current_page
except IndexError:
continue