简介
爬取QQ空间数据(包括说说内容、发表时间、评论数、发布者手机标识)。很很很很很容易上手!
必要准备
作为一个有追求的爬虫,该代码运行需要以下准备:
- selenium包 ,在cmd内输入 ‘pip install Selenium’ ;
- bs4包,输入 ‘pip install bs4’ ;
- pandas包,‘pip install pandas’;
- chrome浏览器 ,下载见 https://www.google.cn/intl/zh-CN/chrome/ 或者其他;
- 对应版本的chromedriver,下载见http://chromedriver.storage.googleapis.com/index.html ,版本对应见 https://blog.youkuaiyun.com/weixin_42730631/article/details/87607958#ChromeDriverChrome_1 下载后放在代码所在目录下;
初始化
friend = '2470288667' # 朋友的QQ号,朋友的空间要求允许你能访问,这里可以输入自己的qq号
user = '' # 你的QQ号
pw = '' # 你的QQ密码
global start_num,goal_num,next_num
start_num = 1 # 设置开始爬取的页码,从头开始则设为0
goal_num = 2000 # 设置爬取的页数
next_num = 1 # 初始“下一页”的id
主要代码
登录
def login(): #登录并跳转至空间说说页面
global driver
driver = webdriver.Chrome() # 获取浏览器驱动
driver.maximize_window() # 浏览器窗口最大化
driver.get("http://i.qq.com") # 浏览器地址定向为qq登陆页面
driver.switch_to.frame("login_frame") # 定位到登录所在的frame
driver.find_element_by_id("switcher_plogin").click() # 自动点击账号登陆方式
driver.find_element_by_id("u").send_keys(user) # 账号输入框输入已知qq账号
driver.find_element_by_id("p").send_keys(pw) # 密码框输入已知密码
driver.find_element_by_id("login_button").click() # 自动点击登陆按钮
time.sleep(1) # 让webdriver操纵当前页
driver.switch_to.default_content()
driver.get("http://user.qzone.qq.com/" + friend + "/311") # 跳到说说的url, friend可以任意改成想访问的空间
try:
button = driver.find_element_by_id("dialog_button_1").click() # 找到关闭按钮,关闭提示框
except:
pass
跳转初始页
def switch_to_startpage(): # 跳转到start_num后开始爬取
global driver
global start_num
for i in range(0, 5):
height = 25000 * i
strWord = "window.scrollBy(0," + str(height) + ")"
driver.execute_script(strWord)
time.sleep(2)
driver.switch_to.frame("app_canvas_frame")
driver.find_element_by_id("pager_go_0").send_keys(str(start_num))
driver.find_element_by_id('pager_gobtn_0').click()
driver.switch_to.parent_frame()
获取数据
def get_data(): # 获取数据
global driver,next_num,goal_num
driver.set_page_load_timeout(60)
driver.set_script_timeout(60)
for i in range(0, 5): # 下拉滚动条,使浏览器加载出全部的内容,
height = 25000 * i # 每次滑动25000像素,分5次完成
strWord = "window.scrollBy(0," + str(height) + ")"
driver.execute_script(strWord)
time.sleep(2)
to_content_txt()
to_analysis_csv()
if driver.page_source.find('pager_next_' + str(next_num)) == -1: # 当已经到了尾页,“下一页”这个按钮就没有id了,可以结束了
break
if next_num == goal_num: # 爬取足够的页数则跳出循环
break
driver.find_element_by_id('pager_next_' + str(next_num)).click() # 找到“下一页”的按钮,因为下一页的按钮是动态变化的,这里需要动态记录一下
next_num += 1 # “下一页”的id
driver.switch_to.parent_frame() # 因为在下一个循环里首先还要把页面下拉,所以要跳到外层的frame上
解析数据
def to_content_txt(): # 源码写入txt
global driver,content
driver.switch_to.parent_frame() # 因为在下一个循环里首先还要把页面下拉,所以要跳到外层的 frame上
driver.switch_to.frame("app_canvas_frame") # 这里需要选中说说所在的frame,否则找不到下面需要的网页元素
content = BeautifulSoup(driver.page_source, "html5lib") # 解析页面元素
with open('content.txt','a',encoding='utf-8') as c: # 写入content
c.write(content.prettify())
def to_analysis_csv(): # 所需数据写入csv
global content
l_ss_contents,l_times,l_phones,l_com = [],[],[],[]
ss = content.find_all('pre') # 说说内容
for i in range(len(ss)):
ss1 = ss[i]
try:
l_ss_contents.append(ss1.contents[0])
except:
l_ss_contents.append('img') # 若有图片,则写入为 img
infos = content.find_all('div','info') # 手机型号、发布时间
for i in range(len(infos)):
info1 = infos[i]
time1 = info1.a.attrs['title']
l_times.append(time1)
phone = info1.find_all('span')[-1].contents[0]
l_phones.append(phone)
comments = content.find_all('div','op') # 评论数
for i in range(len(comments)):
comments[i] = comments[i].find('a',"c_tx comment_btn")
i=0
while True:
while i in range(0,len(comments)):
try: # 排除评论数的干扰项
comment = comments[i].contents[0]
except:
i+=1
break
l_num = []
for j in list(comment): # 提取字符串中的数字并转为int
if j.isdigit():
l_num.append(j)
comment = ''.join(l_num)
if comment != '':
comment = int(comment)
else:
comment = 0
l_com.append(comment)
i += 1
if i > len(comments)-1:
break
d = {'d_comments':l_com,'d_phones':l_phones,'d_ss_contents':l_ss_contents,'d_times':l_times} # 转为字典
try: # 转成dataframe并写入csv
ss_df = pd.DataFrame(d)
ss_df.to_csv('analysis.csv',mode = 'a',encoding = 'utf-8-sig')
except: # 排除因转发使series不等长
pass
运行
if __name__ == '__main__':
login()
switch_to_startpage()
try:
while True:
get_data()
except:
print(next_num+start_num) # 输出停止页面数
作者的话
- 这是小组课程作业,G同学做了很大贡献。
- 爬取部分有参考许多代码,感谢各路大神。(其实很容易看出来写得很乱的那一坨代码是我干的)
- 该代码有缺陷,如假死、数据缺陷。 前者指爬取400~500页数据时可能假死。后者指:1. 如果有转发内容会跳过当前整页数据;2.beautifulsoup 解析时无法识别图片与表情,会影响说说文本内容。
- 此作业是为ddl所逼,尚不完善,欢迎批评指正。