QQ空间说说爬虫
闲来无事,写了一个QQ空间的爬虫,主要是爬取以前的说说,然后生成词云。
这次采用的主要模块是selenium,这是一个模拟浏览器的模块,一开始我不想用这个模块写的,但是后面分析的时候,发现QQ空间的数据加密有点复杂,也没有找到好用的接口,正好又有在学习这个模块,然后就直接用这个模块获取了,这个模块的好处就是不用去纠结传输的过程是如何加密的。
selenium 简介:Selenium 是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Google Chrome,Opera等。这个工具的主要功能包括:测试与浏览器的兼容性——测试你的应用程序看是否能够很好得工作在不同浏览器和操作系统之上。测试系统功能——创建回归测试检验软件功能和用户需求。支持自动录制动作和自动生成 .Net、Java、Perl等不同语言的测试脚本
思路
爬取数据模块
通过selenium模块来模拟浏览器登录QQ空间的操作,进入到说说页面,获取说说的总数目和总页数,从最后一页最后一条,即用户的第一条说说开始爬取,存入MongoDB,重复操作直到爬完最后一条。
词云模块
从数据库读取数据,设置图片(可选),生成词云。
整体思路不难,只是有点地方需要注意下:
- 在进入到QQ空间的登录页面的时候
https://i.qq.com/
先给出的是一个扫码登录的窗口
需要先点击一下『帐号密码登录』才可以进入到输入的界面
还有个比较恶心的地方就是,一开始登录进去的是『个人中心』的界面,那个从个人中心页面选择说说的按钮我一直点击不了,也可能是对JS不太了解的原因,后面直接通过URL跳转到『我的主页』,因为浏览器自带cookie的原因,直接跳转过去是成功的。
我不懂其他账号会不会有这种情况,就是有时候他会弹出黄砖过期的广告,如果不点击的话,就无法进入到下一步的操作,有时候又不弹,所以我这边写了个等待十秒,如果有的话,就关闭,没有就直接进入下一步。
还有一个地方,因为进入到『说说』板块的时候,第一页显示你最近发的说说,因为我想从第一条开始爬,所以跳转到最后一页,然后从最后一条一直往上爬,就可以爬取到第一条说说到最后一条说说,这里出现的问题是,一开始我只是
time.sleep(1)
,导致后面爬取的数据是从第一页先爬,然后再爬最后一页。一开始我调试的时候,没有注意到是时间的问题,在解决这个问题的时候,还花了点时间,后面只需把time.sleep
给延长点就解决了,很奇怪的是,明明显示的是更新后的页面,传送过去的页面并没有是最新的,可能是缓存机制。
剩下的也就没有什么难度了,都是些基本操作,这次的爬虫为了实现模拟是人在使用浏览器进行操作,整体的延迟等待还是会有点多的,不需要的可以自行设置延时时间。
代码
代码如下,该注释的都注释了,基本都能看得懂,也都是些很基础的代码。
# -*-coding:utf-8-*-# Author: AnswerW3I# version:3.6.3from selenium import webdriverfrom selenium.webdriver.chrome.options import Optionsfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import WebDriverWaitimport numpyfrom PIL import Imageimport timefrom bs4 import BeautifulSoupimport matplotlib.pyplot as pltfrom wordcloud import WordCloudimport jiebaimport pymongoclass QZone_Spider(object): def __init__(self, url): self.url = url # self.Browser = webdriver.Chrome() # 可以看到程序的执行流程 self.Browser = self._browser() # 无头模式,看不到流程,提高程序的效率 self.Browser.get(url) self.wait = WebDriverWait(self.Browser, 10) # 显示等待,等待网页加载 self.talks = 1 # 从第一条说说开始计数 self.client = pymongo.MongoClient('localhost', port=27017) # 数据库连接 self.db = self.client.test self.collection = self.db.QQZone def _browser(self): """ :return: 返回一个无头浏览器,禁止加载图片 """ chrome_options = Options() prefs = {'profile.default_content_setting_values':{ 'images':2 } } # 禁止浏览器加载图片,提高浏览器运行速度 chrome_options.add_experimental_option('prefs', prefs) chrome_options.add_argument('window-size=1700x938') # 设置窗口大小,这个很重要,不然无头模式下无法加载页面,会报错 chrome_options.add_argument('--headless') chrome_options.add_argument('--disable-gpu') Browser = webdriver.Chrome(chrome_options=chrome_options) Browser.set_window_size(width=1700, height=1000) return Browser def _login(self, user, password): """ 登录用户 """ self.Browser.switch_to.frame('login_frame') time.sleep(1) login_button = self.Browser.find_element(By.ID, 'switcher_plogin') login_button.click() time.sleep(1) input_name = self.Browser.find_element(By.ID, 'u') input_password = self.Browser.find_element(By.ID, 'p') button = self.Browser.find_element(By.ID, 'login_button') input_name.send_keys(user) time.sleep(1) input_password.send_keys(password) time.sleep(1) button.click() time.sleep(5) self.talks_url = "https://user.qzone.qq.com/" + user + "/main" # qq空间主页 def _get_talks(self, url): """ 获取说说内容 """ self.Browser.get(url) time.sleep(10) self._close_yellow_page(self.Browser.page_source) talks_button = self.Browser.find_element(By.ID, 'QM_Profile_Mood_Cnt') self.talks_num = talks_button.text print("talks:"+ self.talks_num) talks_button.click() time.sleep(5) self.Browser.switch_to.frame('app_canvas_frame') # 进入到说说的frame self.pages = self._get_all_pages() for page in range(self.pages, 0, -1): # 从最后一页开始,也就是第一条说说开始爬取 print("开始爬取第{0}页".format(page)) # 获取当前页面的说说 self._get_page(page=page) print("第{0}页爬取完毕".format(page)) time.sleep(5) print("OK!") def _close_yellow_page(self, html): # 关闭黄砖广告 soup = BeautifulSoup(html, 'lxml') page = soup.find_all(id="dialog_main_1") if page != []: self.Browser.find_element(By.CLASS_NAME, 'qz_dialog_btn_close').click() def _says(self, html): """ 爬取说说 :param html: 当前说说的html页面 """ soup = BeautifulSoup(html, 'lxml') print("start with {0}".format(self.Browser.find_element(By.CLASS_NAME, 'mod_pagenav_main').find_element(By.CLASS_NAME, 'current').text)) says = soup.select('.feed') for item in says[::-1]: # 从说说列表的下面开始爬取 print("第{0}条爬取成功".format(self.talks)) self.talks = self.talks + 1 yield { "say": item.select('.content')[0].text, # 说说内容 "date": item.select('.ft .goDetail')[0]['title'] # 说说发布的时间 } def _get_all_pages(self): """ :return: 说说总页数 """ return int(self.Browser.find_element(By.ID, 'pager_last_0').text) def _get_page(self, page): change_page = self.Browser.find_element(By.CLASS_NAME, 'mod_pagenav_option').find_element(By.CLASS_NAME, 'textinput') change_page_button = self.Browser.find_element(By.CLASS_NAME, 'mod_pagenav_option').find_element(By.CLASS_NAME, 'bt_tx2') change_page.send_keys(page) time.sleep(1) change_page_button.click() # 进入下一页 self.wait = WebDriverWait(self.Browser, 10) print(self.Browser.find_element(By.CLASS_NAME, 'mod_pagenav_main').find_element(By.CLASS_NAME, 'current').text) time.sleep(10) # 这个很重要,不然加载不出新的页面... for item in self._says(self.Browser.page_source): self._save(item) # print(item) def _save(self, data): """ 保存到MongDB里面 """ self.collection.insert(data)class FenCI(object): def __init__(self): self.client = pymongo.MongoClient("localhost", port=27017) self.db = self.client.test self.collection = self.db.QQZone def _get_words(self): words = "" for item in self.collection.find({}): for i in self._cut_words(item['say']): words = words + i + " " return words def _cut_words(self, data): for item in jieba.cut(data, cut_all=False): try: yield item except Exception as err: # 这里有时候会爬取到emoji表情,导致编码读取的时候会报错,这里直接pass掉 pass def _wordColud(self): """ 显示词云 """ world_picture = numpy.array(Image.open("C:/Users/Desktop/img.jpg")) # 加载图片的路径,可选项,不需要图片的话,把下面WorCloud下的mask去掉。 wl_space_split = self._get_words()# 获取生成图文的文字 font = r'C:\Windows\Fonts\simfang.ttf' my_wordcloud = WordCloud(background_color="white", collocations=False, font_path=font, width=1400, height=1400, margin=2, mask=world_picture).generate(wl_space_split) # 设置图片大小 plt.imshow(my_wordcloud) plt.axis("off") plt.show() plt.savefig('QQZone.png') # 保存图片def main(): url = "https://i.qq.com/" Spider = QZone_Spider(url) User = input("User:") Password = input("Password:") Spider._login(user=User, password=Password) Spider._get_talks(Spider.talks_url) # 显示词云 Picture = FenCI() Picture._wordColud()if __name__ =="__main__": main()
后记
感觉博客还是得多写,不然感觉文章格式都成一个问题。
这篇文章本该前几天就发了的...一直拖拖到现在...然后最近这段时间也有点迷茫,希望接下来能好好调整一下