學校有個老師想研究微博《肺炎患者求助》的文本信息,他給了我一個PC端的鏈接,找我幫忙寫爬蟲,把鏈接上所有求助信息全部爬下來,我查看一共有21頁,日期爲2020年2月1日~2020年3月13日;經過一番檢查後,我決定自己從移動端網頁入手,其一:因爲我上個月爬取過微博的《戰疫情》,可以節約很多時間去分析網頁結構;其二:移動端使用的是ajax加載,請求得到json數據,速度快得很多。在這裏我就不去詳細講解這次的實戰了,如果感興趣的話可以結合我上次的實戰經驗閱讀。
曾經實戰項目: python爬蟲爬取微博之戰疫情用戶評論及詳情
PC端網頁: https://weibo.com/p/1008084882401a015244a2ab18ee43f7772d6f/super_index
操作環境: windows10, python37, jupyter
實現思路:
- 打開《肺炎患者求助》首頁,進行抓包,分析它的json結構;
- 通過抓包可以拿到所有的文章的:用戶,發佈時間,文章ID轉發量,評論量,點贊量等等,但是無法拿到超出發佈內容範圍的隱藏部分;
- 隱藏內容超出部分的文章有一個“全文”的標識,鼠標點擊後即可展開,但是我們只需要自己通過上面拿到的文章ID拼接出它的完整鏈接,即可訪問詳情信息;同時,是否要進行訪問詳情信息可以通過“【姓名】”是否存在進行,這樣可以節約程序的時間,提高效率。
- 使用正則表達式匹配出上面的內容的詳細信息,按照:“姓名、年齡、城市、地址、時間、聯繫方式、緊急聯繫人、病情描述" 這樣來存儲;
- 下一個API中的區別在
since_id
,可以在當前API中獲取,變化since_id
就可以不斷的進行下一個json數據的獲取; - 在
while True
中設置了兩個跳出死循環的條件,一個爲找不到下一個since_id
時跳出循環,第二個爲since_id
以前面的重複時跳出死循環。 - 結果寫入CSV.
import requests, csv, time, re
startTime = time.time() #記錄起始時間
csvfile = open('./微博疫情求助+數據匹配.csv', 'a', newline='', encoding = 'utf-8-sig')
writer = csv.writer(csvfile)
writer.writerow(('文章ID', '發佈時間', '轉發量', '評論數', '點贊數', '內容','姓名', '年齡', '城市', '小區', '患病時間', '病情描述', '聯繫方式', '其他緊急聯繫人'))
index_url = "https://m.weibo.cn/api/container/getIndex?containerid=1008084882401a015244a2ab18ee43f7772d6f_-_feed&luicode=10000011&lfid=100103type%3D1%26q%3D%E8%82%BA%E7%82%8E%E6%82%A3%E8%80%85%E6%B1%82%E5%8A%A9%E8%B6%85%E8%AF%9D&display=0&retcode=6102"
next_id_list = []
def regular(find_title):
name = re.findall('.*?【姓名】(.*?)<', find_title)
if len(name) != 0:
name = ''.join(name)
else:
name = ""
age = re.findall('.*?【年齡】(.*?)<', find_title)
if len(age) != 0:
age = ''.join(age)
else:
age = ""
city = re.findall('.*?【所在城市】(.*?)<', find_title)
if len(city) != 0:
city = ''.join(city)
else:
city = ""
village = re.findall('.*?【所在小區、社區】(.*?)<', find_title)
if len(village) != 0:
village = ''.join(village)
else:
village = ""
ill_time = re.findall('.*?【患病時間】(.*?)<', find_title)
if len(ill_time) != 0:
ill_time = ''.join(ill_time)
else:
ill_time = ""
describe = re.findall('.*?【病情描述】(.*?)<', find_title)
if len(describe) != 0:
describe = ''.join(describe)
else:
describe = ""
phone = re.findall('.*?【聯繫方式】(.*?)<', find_title)
if len(phone) != 0:
phone = ''.join(phone)
else:
phone = ""
other_way = re.findall('.*?【其他緊急聯繫人】(.*?)<', find_title)
if len(other_way) != 0:
other_way = ''.join(other_way)
else:
other_way = ""
return name, age, city, village, ill_time, describe, phone, other_way
while True:
html = requests.get(url = index_url)
try:
since_id = html.json()["data"]["pageInfo"]["since_id"]
except:
break
index_url = index_url + "&since_id=" + str(since_id)
# 防止since_id重複,當since_id重複時跳出循環
if since_id in next_id_list:
break
else:
next_id_list.append(since_id)
# 提取所有信息
for i in html.json()["data"]["cards"]:
try:
for j in i["card_group"]:
title_id = j["mblog"]["id"] #文章ID
created_time = j["mblog"]["created_at"] #發佈時間
sharing = j["mblog"]["reposts_count"] #轉發量
comments_count = j["mblog"]["comments_count"] #評論數
great = j["mblog"]["attitudes_count"] #點贊
comments_html = j["mblog"]["text"] # 內容,html形式的
if ">全文<" in comments_html:
all_url = "https://m.weibo.cn/status/" + title_id #打開全文,獲取具體信息
html_text = requests.get(url=all_url).text
# 話題內容
find_title = re.findall('.*?"text": "(.*?)",.*?', html_text)[0]
comment_text = re.sub('<(S*?)[^>]*>.*?|<.*? />', '', find_title) #正則匹配掉html標籤
if "【姓名】" in find_title:
result = regular(find_title)
else:
comment_text = re.sub('<(S*?)[^>]*>.*?|<.*? />', '', comments_html) #正則匹配掉html標籤
if "【姓名】" in find_title:
result = regular(find_title)
if "【姓名】" in comments_html:
writer.writerow((title_id, created_time, sharing, comments_count, great, comment_text, result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7]))
else:
writer.writerow((title_id, created_time, sharing, comments_count, great, comment_text))
except:
pass
print ("正在爬取:", since_id)
csvfile.close() #關閉文件
endTime =time.time()#獲取結束時的時間
useTime =(endTime-startTime)/60
print ("該次所獲的信息一共使用%s分鐘"%useTime)
jupyter運行結果:
CSV結果: