0%

CrawlerOnWeibo

项目背景:出于需求微博部分大V提问信息、问题价值、问题回复以及对应回答人员的信息的爬取用于科研,不涉及反反爬内容,仅用最基本功能等。
Based on: Python 3.8


Quite Start


Page One Get

从EXCEL中读取对应大V的问答链接,进行其所有问答链接的爬取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import requests
from openpyxl import Workbook
from openpyxl import load_workbook
# import pymysql
import pandas as pd

#请求头
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36',
'Referer': 'https://m.weibo.cn/'
}

# 定义函数获取多页数据,两个参数分别是下一页面的since_id和爬取的博主ID
def get_page(since_id,bigV_id):
#url变化的部分固定为传入的两个参数的字符串格式str()
url = 'https://m.weibo.cn/api/container/getIndex?containerid=231068_-_QUESTIONLIST&extparam=' + str(bigV_id) + '&since_id=' + str(since_id)
try:
#获取页面的请求许可
response = requests.get(url, headers=headers)
#如果请求返回状态码为200表示许可
if response.status_code == 200:
#以json文件解析请求数据包
json = response.json()
if json['ok'] == 1:
#如果成功则获取json文件中的cardlistInfo下的since_id
next_since_id = json.get('data').get('cardlistInfo')['since_id']
#返回下一页面的since
return (json, next_since_id)
else:
return None
#下面是出错退出返回
except requests.ConnectionError as e:
print('Error', e.args)



#此代码定义函数run()来获取需要的数据,传入参数为需要爬取的微博用户
def run(bigV_id):
# 创建一个新的Excel工作簿,设置列名称,和工作表名
newWb = Workbook()
newWs = newWb.create_sheet('1', 0)
# 添加表头
newWs.append(['uid', 'asker_id', 'name_ask', 'avatar', 'onlookers', 'brief_ques', 'value', 'detailed_url'])
#设置一个值表示当前页面,初始为0
i = 0


# while True:
while True:
if i == 0: # 未滚动,当前为初始页面
#创建元组tuple_since_id存当前初始页面的数据
tuple_since_id = get_page('',bigV_id)
# 页面加一
i+=1
#如果已经不是初始页面,开始数据搜集
else:

tuple_since_id = get_page(tuple_since_id[1],bigV_id)

if tuple_since_id: # 如果获取元组数据
print(tuple_since_id[1])

# 检查新的 since_id 是否为空,如果是,说明只有一个页面,只获取一次数据用break结束
if tuple_since_id[1] =='':#这里是单页面的条件,since_id=''
#获取元组内的数据解析json
json = tuple_since_id[0]
# 解析JSON数据提取所需信息,我们需要的事data,card中的部分数据
cards = json['data']['cards']
#遍历card中的数据,挨个获取
for card in cards:
#要的事card中的cardgroup的数据
if 'card_group' in card:
row = [] # 建立一个价row的列表,用于存储合并后的一行数据
# 遍历cardgroup中的数据,挨个获取
for item in card['card_group']:
#需要card_type==93下的数据
if item['card_type'] == 93:
#获取user的id
user = item['user']
if user:#如果获取到
#列表中的数据对应连续添加到Excel的列中,split是必要的内容切分只获取需要的
row.extend([bigV_id, user['id'], user['screen_name'], user['profile_image_url'],
item['status'].split('</font><font')[0].split('>')[-1]])
else:#如果没有就置空
row.extend(['', '', '', ''])
#下面同理也是获取card的数据
elif item['card_type'] == 8:
row.extend([item['title_sub'], item['desc1'], item['scheme']])
print(item['title_sub'])
newWs.append(row)#增加新的一行
print("一页的数据获取完毕,结束")
#单页面的数据获取完毕,结束
break

#接下来就是获取需要滚动加载的多页面包的数据,逻辑一致,只是没有结束条件
else:

json = tuple_since_id[0]
# 解析JSON数据提取所需信息
cards = json['data']['cards']
for card in cards:
if 'card_group' in card:
row = [] # 用于存储合并后的一行数据
for item in card['card_group']:
if item['card_type'] == 93:
user = item['user']
if user:
row.extend([bigV_id,user['id'], user['screen_name'], user['profile_image_url'],item['status'].split('</font><font')[0].split('>')[-1]])
else:
row.extend(['','','',''])
elif item['card_type'] == 8:
row.extend([item['title_sub'], item['desc1'], item['scheme']])
print(item['title_sub'])
newWs.append(row)

else:
break
# 滚动到底跳出循环

#Excel表格保存save()函数,命名为用户名.xlsx
newWb.save(str(bigV_id) + '.xlsx')


#main函数,说明这个工作表的路径和要处理的开始行
if __name__ == "__main__":
file_path = r"./merged_file.xlsx"

# 读取Excel文件的第2行的第一列数据
df = pd.read_excel(file_path, usecols=[0], header=None)
ids = df.iloc[305:350, 0].tolist()
print(ids)
#调用run()函数
# run(1990466285)
#

# #调用run()函数
for id in ids:
run(id)

Page Two Get

从上述代码获得问答详细链接后进行下一步问答详细信息提取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# 使用import导入requests模块
import time
import re
import pandas as pd
import requests
from openpyxl import load_workbook
# 从bs4中导入BeautifulSoup
from bs4 import BeautifulSoup


def run(name: str):
# 打开Excel文件
# 通过 load_workbook 函数打开指定的 Excel 文件,并获取活动工作表 ws
# 注意每次处理的时候修改工作表名字,比如当前的工作表名为“test_1.xlsx”
# excel_name = 'test_1.xlsx'

count = 0

excel_name = name

wb = load_workbook(excel_name)
ws = wb.active

# 遍历Excel里面的所有工作表
for ws in wb.worksheets:
print("当前工作表名称:", ws.title)
# 获取工作表的最大列数
max_column = ws.max_column
# 设置表头,添加三列
ws.cell(row=1, column=9, value='time_ask')
ws.cell(row=1, column=10, value='time_ans')
ws.cell(row=1, column=11, value='ques')

# 用函数enumerate获取列索引(当前列),和需要读取的列,规定从第二行开始
for col_index, col in enumerate(ws.iter_cols(min_col=8, max_col=8, min_row=2, values_only=True), start=2):
# 每一行的当前行数,获取page_id,从第二行开始
for row_index, page_id in enumerate(col, start=2):
# page_id为二级页面需要的url
if page_id == ' ':
break
print("元素所在行:", row_index)

# 将表格中的page_id传给url
url = f'{page_id}'

# 请求头
headers = {
"User-Agent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',
# 'Referer': 'https://media.weibo.cn/api/wenda?',
# 补充你的cookie
"Cookie": ""
}
# 获取反馈
response = requests.get(url, headers=headers)
# 解码,防止中文乱码
response.encoding = response.apparent_encoding

if response.status_code == 200:

# 将服务器响应内容转换为字符串形式,赋值给html
html = response.text
# print(html)
# 使用BeautifulSoup()传入变量html和解析器lxml,赋值给soup
soup = BeautifulSoup(html, "lxml")
# 使用find_all()查询soup中class=" "的节点

# 获取title结构下的内容,即为需要的详细评论
content_all = soup.find_all("title")

for content in content_all:
# 获取内容
text = content.text
if '抱歉,出错了' in text:
text = '博主设置无法查看'
count += 1
print(text)
# 你要插入的行号,在一开始我们获取到的row_index
row_number = row_index
# 将提问的时间填写到对应行的第11列单元格中,根据表格调整
ws.cell(row=row_number, column=11, value=text)
# 保存工作表
wb.save(excel_name)

# 获取提问时间和回答时间,是标记在DIV中间,CLASS_名为“text S_txt2"的内容
div_all = soup.find_all("div", class_="text S_txt2")
# 建一个列表来存
time_all = []
# 遍历存,连续地写到列表中
for div in div_all:
text = div.get_text(strip=True) # 使用strip=True去除文本前后的空白字符
time_all.append(text)

# 使用正则表达式提取时间部分,符合该格式的才会被提取出来,可以看到{}内的使我们提取的信息
pattern_Q = r'(\d{2}-\d{2} \d{2}:\d{2} )提问'
pattern_A = r'(\d{2}-\d{2} \d{2}:\d{2} )回答'

# 然后再使用正则表达式提取时间部分
for time_text in time_all:
match_A = re.search(pattern_A, time_text)
match_Q = re.search(pattern_Q, time_text)

if match_A:
# 获取answer回答的时间
time_answer = match_A.group(1)
print(time_answer)
# 你要插入的行号
row_number = row_index
# row_number = 2
# 将回答的时间填写到对应行的第10列单元格中
ws.cell(row=row_number, column=10, value=time_answer)
# 保存工作表
wb.save(excel_name)

if match_Q:
# 获取question提问的时间
time_ask = match_Q.group(1)
print(time_ask)

# 假设你要插入的行号是2(示例)
row_number = row_index
# row_number = 2

# 将提问的时间填写到对应行的第9列单元格中
ws.cell(row=row_number, column=9, value=time_ask)
# 保存工作表
wb.save(excel_name)

# time.sleep(1)

if count >= 5:
wb.save("./many/" + excel_name)
# 保存工作表
else:
wb.save("./less/" + excel_name)


if __name__ == "__main__":
file_path = r"./merged_file.xlsx"
# 读取Excel文件的第2行的第一列数据
df = pd.read_excel(file_path, usecols=[0], header=None)
ids = df.iloc[292:350, 0].tolist()
print(ids, "\n")
# 调用run()函数
for id in ids:
try:
print(id, type(id))
# run(str(id) + '.xlsx')
name = "{}".format(id) + ".xlsx"
print(name, type(name), "\n")
run(name=name)

except:
continue

End Note

仅仅如此,简单的一个小作坊手搓easy代码完成,能跑就行。略显潦草,后续有兴趣可以进行下一步的数据清洗、存储数据库、进行反反爬等。