python爬虫(17)爬出新高度_抓取微信公众号文章(selenium+phantomjs)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wufaliang003/article/details/81988981

一.思路分析

目前所知晓的能够抓取的方法有:

1、微信APP中微信公众号文章链接的直接抓取(http://mp.weixin.qq.com/s?__biz=MjM5MzU4ODk2MA==&mid=2735446906&idx=1&sn=ece37deaba0c8ebb9badf07e5a5a3bd3&scene=0#rd)


2、通过微信合作方搜狗搜索引擎(http://weixin.sogou.com/),发送相应请求来间接抓取

第1种方法中,这种链接不太好获取,而且他的规律不是特别清晰。

因此本文采用的是方法2----通过给 weixin.sogou.com 发送即时请求来实时解析抓取数据并保存到本地。

二.爬取过程

1、首先在搜狗的微信搜索页面测试一下,这样能够让我们的思路更加清晰

在搜索引擎上使用微信公众号英文名进行“搜公众号”操作(因为公众号英文名是公众号唯一的,而中文名可能会有重复,同时公众号名字一定要完全正确,不然可能搜到很多东西,这样我们可以减少数据的筛选工作,只要找到这个唯一英文名对应的那条数据即可),即发送请求到'http://weixin.sogou.com/weixin?type=1&query=%s&ie=utf8&_sug_=n&_sug_type_= ' %  'python',并从页面中解析出搜索结果公众号对应的主页跳转链接。

2.获取主页入口内容

使用request , urllib,urllib2,或者直接使用webdriver+phantomjs等都可以

这里使用的是request.get()的方法获取入口网页内容

 
  1. # 爬虫伪装头部设置

  2. self.headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0'}

  3.  
  4. # 设置操作超时时长

  5. self.timeout = 5

  6.  
  7. # 爬虫模拟在一个request.session中完成

  8. self.s = requests.Session()

 
  1. #搜索入口地址,以公众为关键字搜索该公众号

  2. def get_search_result_by_keywords(self):

  3. self.log('搜索地址为:%s' % self.sogou_search_url)

  4. return self.s.get(self.sogou_search_url, headers=self.headers, timeout=self.timeout).content

3.获取公众号地址

从获取到的网页内容中,得到公众号主页地址, 这一步骤有很多方法, beautifulsoup、webdriver,直接使用正则,pyquery等都可以

这里使用的是pyquery的方法来查找公众号主页入口地址

 
  1. #获得公众号主页地址

  2. def get_wx_url_by_sougou_search_html(self, sougou_search_html):

  3. doc = pq(sougou_search_html)

  4. #print doc('p[class="tit"]')('a').attr('href')

  5. #print doc('div[class=img-box]')('a').attr('href')

  6. #通过pyquery的方式处理网页内容,类似用beautifulsoup,但是pyquery和jQuery的方法类似,找到公众号主页地址

  7. return doc('div[class=txt-box]')('p[class=tit]')('a').attr('href')

4.获取公众号主页的文章列表

首先需要加载公众号主页,这里用的是phantomjs+webdriver, 因为这个主页的内容需要JS 渲染加载,采用之前的方法只能获得静态的网页内容

 
  1. #使用webdriver 加载公众号主页内容,主要是js渲染的部分

  2. def get_selenium_js_html(self, url):

  3. browser = webdriver.PhantomJS()

  4. browser.get(url)

  5. time.sleep(3)

  6. # 执行js得到整个页面内容

  7. html = browser.execute_script("return document.documentElement.outerHTML")

  8. return html

 得到主页内容之后,获取文章列表,这个文章列表中有我们需要的内容

 
  1. #获取公众号文章内容

  2. def parse_wx_articles_by_html(self, selenium_html):

  3. doc = pq(selenium_html)

  4. print '开始查找内容msg'

  5. return doc('div[class="weui_media_box appmsg"]')

  6.  
  7. #有的公众号仅仅有10篇文章,有的可能多一点

  8. #return doc('div[class="weui_msg_card"]')#公众号只有10篇文章文章的

5.解析每一个文章列表,获取我们需要的信息

6.处理对应的内容

包括文章名字,地址,简介,发表时间等

7.保存文章内容

以html的格式保存到本地

同时将上一步骤的内容保存成excel 的格式

8.保存json数据

这样,每一步拆分完,爬取公众号的文章就不是特别难了。

三、源码

第一版源码如下:

 
  1. #!/usr/bin/python

  2. # coding: utf-8

  3.  
  4. import sys

  5. reload(sys)

  6. sys.setdefaultencoding('utf-8')

  7.  
  8. from urllib import quote

  9. from pyquery import PyQuery as pq

  10. from selenium import webdriver

  11.  
  12. import requests

  13. import time

  14. import re

  15. import json

  16. import os

  17.  
  18.  
  19. class weixin_spider:

  20. def __init__(self, kw):

  21. ' 构造函数 '

  22. self.kw = kw

  23. # 搜狐微信搜索链接

  24. #self.sogou_search_url = 'http://weixin.sogou.com/weixin?type=1&query=%s&ie=utf8&_sug_=n&_sug_type_=' % quote(self.kw)

  25. self.sogou_search_url = 'http://weixin.sogou.com/weixin?type=1&query=%s&ie=utf8&s_from=input&_sug_=n&_sug_type_=' % quote(self.kw)

  26.  
  27. # 爬虫伪装

  28. self.headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 FirePHP/0refox/47.0 FirePHP/0.7.4.1'}

  29.  
  30. # 操作超时时长

  31. self.timeout = 5

  32. self.s = requests.Session()

  33.  
  34. def get_search_result_by_kw(self):

  35. self.log('搜索地址为:%s' % self.sogou_search_url)

  36. return self.s.get(self.sogou_search_url, headers=self.headers, timeout=self.timeout).content

  37.  
  38. def get_wx_url_by_sougou_search_html(self, sougou_search_html):

  39. ' 根据返回sougou_search_html,从中获取公众号主页链接 '

  40. doc = pq(sougou_search_html)

  41. #print doc('p[class="tit"]')('a').attr('href')

  42. #print doc('div[class=img-box]')('a').attr('href')

  43. #通过pyquery的方式处理网页内容,类似用beautifulsoup,但是pyquery和jQuery的方法类似,找到公众号主页地址

  44. return doc('div[class=txt-box]')('p[class=tit]')('a').attr('href')

  45.  
  46. def get_selenium_js_html(self, wx_url):

  47. ' 执行js渲染内容,并返回渲染后的html内容 '

  48. browser = webdriver.PhantomJS()

  49. browser.get(wx_url)

  50. time.sleep(3)

  51. # 执行js得到整个dom

  52. html = browser.execute_script("return document.documentElement.outerHTML")

  53. return html

  54.  
  55. def parse_wx_articles_by_html(self, selenium_html):

  56. ' 从selenium_html中解析出微信公众号文章 '

  57. doc = pq(selenium_html)

  58. return doc('div[class="weui_msg_card"]')

  59.  
  60. def switch_arctiles_to_list(self, articles):

  61. ' 把articles转换成数据字典 '

  62. articles_list = []

  63. i = 1

  64.  
  65. if articles:

  66. for article in articles.items():

  67. self.log(u'开始整合(%d/%d)' % (i, len(articles)))

  68. articles_list.append(self.parse_one_article(article))

  69. i += 1

  70. # break

  71.  
  72. return articles_list

  73.  
  74. def parse_one_article(self, article):

  75. ' 解析单篇文章 '

  76. article_dict = {}

  77.  
  78. article = article('.weui_media_box[id]')

  79.  
  80. title = article('h4[class="weui_media_title"]').text()

  81. self.log('标题是: %s' % title)

  82. url = 'http://mp.weixin.qq.com' + article('h4[class="weui_media_title"]').attr('hrefs')

  83. self.log('地址为: %s' % url)

  84. summary = article('.weui_media_desc').text()

  85. self.log('文章简述: %s' % summary)

  86. date = article('.weui_media_extra_info').text()

  87. self.log('发表时间为: %s' % date)

  88. pic = self.parse_cover_pic(article)

  89. content = self.parse_content_by_url(url).html()

  90.  
  91. contentfiletitle=self.kw+'/'+title+'_'+date+'.html'

  92. self.save_content_file(contentfiletitle,content)

  93.  
  94. return {

  95. 'title': title,

  96. 'url': url,

  97. 'summary': summary,

  98. 'date': date,

  99. 'pic': pic,

  100. 'content': content

  101. }

  102.  
  103. def parse_cover_pic(self, article):

  104. ' 解析文章封面图片 '

  105. pic = article('.weui_media_hd').attr('style')

  106.  
  107. p = re.compile(r'background-image:url\((.*?)\)')

  108. rs = p.findall(pic)

  109. self.log( '封面图片是:%s ' % rs[0] if len(rs) > 0 else '')

  110.  
  111. return rs[0] if len(rs) > 0 else ''

  112.  
  113. def parse_content_by_url(self, url):

  114. ' 获取文章详情内容 '

  115. page_html = self.get_selenium_js_html(url)

  116. return pq(page_html)('#js_content')

  117.  
  118. def save_content_file(self,title,content):

  119. ' 页面内容写入文件 '

  120. with open(title, 'w') as f:

  121. f.write(content)

  122.  
  123. def save_file(self, content):

  124. ' 数据写入文件 '

  125. with open(self.kw+'/'+self.kw+'.txt', 'w') as f:

  126. f.write(content)

  127.  
  128. def log(self, msg):

  129. ' 自定义log函数 '

  130. print u'%s: %s' % (time.strftime('%Y-%m-%d %H:%M:%S'), msg)

  131.  
  132. def need_verify(self, selenium_html):

  133. ' 有时候对方会封锁ip,这里做一下判断,检测html中是否包含id=verify_change的标签,有的话,代表被重定向了,提醒过一阵子重试 '

  134. return pq(selenium_html)('#verify_change').text() != ''

  135.  
  136. def create_dir(self):

  137. '创建文件夹'

  138. if not os.path.exists(self.kw):

  139. os.makedirs(self.kw)

  140.  
  141.  
  142. def run(self):

  143. ' 爬虫入口函数 '

  144. #Step 0 : 创建公众号命名的文件夹

  145. self.create_dir()

  146.  
  147. # Step 1:GET请求到搜狗微信引擎,以微信公众号英文名称作为查询关键字

  148. self.log(u'开始获取,微信公众号英文名为:%s' % self.kw)

  149. self.log(u'开始调用sougou搜索引擎')

  150. sougou_search_html = self.get_search_result_by_kw()

  151.  
  152. # Step 2:从搜索结果页中解析出公众号主页链接

  153. self.log(u'获取sougou_search_html成功,开始抓取公众号对应的主页wx_url')

  154. wx_url = self.get_wx_url_by_sougou_search_html(sougou_search_html)

  155. self.log(u'获取wx_url成功,%s' % wx_url)

  156.  
  157.  
  158. # Step 3:Selenium+PhantomJs获取js异步加载渲染后的html

  159. self.log(u'开始调用selenium渲染html')

  160. selenium_html = self.get_selenium_js_html(wx_url)

  161.  
  162. # Step 4: 检测目标网站是否进行了封锁

  163. if self.need_verify(selenium_html):

  164. self.log(u'爬虫被目标网站封锁,请稍后再试')

  165. else:

  166. # Step 5: 使用PyQuery,从Step 3获取的html中解析出公众号文章列表的数据

  167. self.log(u'调用selenium渲染html完成,开始解析公众号文章')

  168. articles = self.parse_wx_articles_by_html(selenium_html)

  169. self.log(u'抓取到微信文章%d篇' % len(articles))

  170.  
  171. # Step 6: 把微信文章数据封装成字典的list

  172. self.log(u'开始整合微信文章数据为字典')

  173. articles_list = self.switch_arctiles_to_list(articles)

  174.  
  175. # Step 7: 把Step 5的字典list转换为Json

  176. self.log(u'整合完成,开始转换为json')

  177. data_json = json.dumps(articles_list)

  178.  
  179. # Step 8: 写文件

  180. self.log(u'转换为json完成,开始保存json数据到文件')

  181. self.save_file(data_json)

  182.  
  183. self.log(u'保存完成,程序结束')

  184.  
  185. # main

  186. if __name__ == '__main__':

  187.  
  188. gongzhonghao=raw_input(u'输入要爬取的公众号')

  189. if not gongzhonghao:

  190. gongzhonghao='python6359'

  191. weixin_spider(gongzhonghao).run()

第二版代码:

对代码进行了一些优化和整改,主要:

1.增加了excel存贮

2.对获取文章内容规则进行修改

3.丰富了注释

本程序已知缺陷: 如果公众号的文章内容包括视视频,可能会报错。

 
  1. #!/usr/bin/python

  2. # coding: utf-8

  3.  
  4. '''

  5. 总的来说就是通过搜狗搜索中的微信搜索入口来爬取

  6. 2017-04-12 by Jimy_fengqi

  7. '''

  8.  
  9. #这三行代码是防止在python2上面编码错误的,在python3上面不要要这样设置

  10. import sys

  11. reload(sys)

  12. sys.setdefaultencoding('utf-8')

  13.  
  14. from urllib import quote

  15. from pyquery import PyQuery as pq

  16. from selenium import webdriver

  17. from pyExcelerator import * #导入excel相关包

  18.  
  19. import requests

  20. import time

  21. import re

  22. import json

  23. import os

  24.  
  25.  
  26. class weixin_spider:

  27.  
  28. def __init__(self, keywords):

  29. ' 构造函数 '

  30. self.keywords = keywords

  31. # 搜狐微信搜索链接入口

  32. #self.sogou_search_url = 'http://weixin.sogou.com/weixin?type=1&query=%s&ie=utf8&_sug_=n&_sug_type_=' % quote(self.keywords)

  33. self.sogou_search_url = 'http://weixin.sogou.com/weixin?type=1&query=%s&ie=utf8&s_from=input&_sug_=n&_sug_type_=' % quote(self.keywords)

  34.  
  35. # 爬虫伪装头部设置

  36. self.headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0'}

  37.  
  38. # 设置操作超时时长

  39. self.timeout = 5

  40.  
  41. # 爬虫模拟在一个request.session中完成

  42. self.s = requests.Session()

  43.  
  44. #excel 第一行数据

  45. self.excel_data=[u'编号',u'时间',u'文章标题',u'文章地址',u'文章简介']

  46. #定义excel操作句柄

  47. self.excle_w=Workbook()

  48.  
  49.  
  50. #搜索入口地址,以公众为关键字搜索该公众号

  51. def get_search_result_by_keywords(self):

  52. self.log('搜索地址为:%s' % self.sogou_search_url)

  53. return self.s.get(self.sogou_search_url, headers=self.headers, timeout=self.timeout).content

  54.  
  55. #获得公众号主页地址

  56. def get_wx_url_by_sougou_search_html(self, sougou_search_html):

  57. doc = pq(sougou_search_html)

  58. #print doc('p[class="tit"]')('a').attr('href')

  59. #print doc('div[class=img-box]')('a').attr('href')

  60. #通过pyquery的方式处理网页内容,类似用beautifulsoup,但是pyquery和jQuery的方法类似,找到公众号主页地址

  61. return doc('div[class=txt-box]')('p[class=tit]')('a').attr('href')

  62.  
  63. #使用webdriver 加载公众号主页内容,主要是js渲染的部分

  64. def get_selenium_js_html(self, url):

  65. browser = webdriver.PhantomJS()

  66. browser.get(url)

  67. time.sleep(3)

  68. # 执行js得到整个页面内容

  69. html = browser.execute_script("return document.documentElement.outerHTML")

  70. return html

  71. #获取公众号文章内容

  72. def parse_wx_articles_by_html(self, selenium_html):

  73. doc = pq(selenium_html)

  74. print '开始查找内容msg'

  75. return doc('div[class="weui_media_box appmsg"]')

  76.  
  77. #有的公众号仅仅有10篇文章,有的可能多一点

  78. #return doc('div[class="weui_msg_card"]')#公众号只有10篇文章文章的

  79.  
  80.  
  81. #将获取到的文章转换为字典

  82. def switch_arctiles_to_list(self, articles):

  83. #定义存贮变量

  84. articles_list = []

  85. i = 1

  86.  
  87. #以当前时间为名字建表

  88. excel_sheet_name=time.strftime('%Y-%m-%d')

  89. excel_content=self.excle_w.add_sheet(excel_sheet_name)

  90.  
  91. #遍历找到的文章,解析里面的内容

  92. if articles:

  93. for article in articles.items():

  94. self.log(u'开始整合(%d/%d)' % (i, len(articles)))

  95. #处理单个文章

  96. articles_list.append(self.parse_one_article(article,i,excel_content))

  97. i += 1

  98. return articles_list

  99. #解析单篇文章

  100. def parse_one_article(self, article,i,excel_content):

  101. article_dict = {}

  102.  
  103. #获取标题

  104. title = article('h4[class="weui_media_title"]').text()

  105. self.log('标题是: %s' % title)

  106. #获取标题对应的地址

  107. url = 'http://mp.weixin.qq.com' + article('h4[class="weui_media_title"]').attr('hrefs')

  108. self.log('地址为: %s' % url)

  109. #获取概要内容

  110. summary = article('.weui_media_desc').text()

  111. self.log('文章简述: %s' % summary)

  112. #获取文章发表时间

  113. date = article('.weui_media_extra_info').text()

  114. self.log('发表时间为: %s' % date)

  115. #获取封面图片

  116. pic = self.parse_cover_pic(article)

  117. #获取文章内容

  118. content = self.parse_content_by_url(url).html()

  119. #存储文章到本地

  120. contentfiletitle=self.keywords+'/'+title+'_'+date+'.html'

  121. self.save_content_file(contentfiletitle,content)

  122.  
  123. #将这些简单的信息保存成excel数据

  124. cols=0

  125. tempContent=[i,date,title,url,summary]

  126. for data in self.excel_data:

  127. excel_content.write(0,cols,data)

  128. excel_content.write(i,cols,tempContent[cols])

  129.  
  130. cols +=1

  131. self.excle_w.save(self.keywords+'/'+self.keywords+'.xls')

  132.  
  133.  
  134. #返回字典数据

  135. return {

  136. 'title': title,

  137. 'url': url,

  138. 'summary': summary,

  139. 'date': date,

  140. 'pic': pic,

  141. 'content': content

  142. }

  143.  
  144. #查找封面图片,获取封面图片地址

  145. def parse_cover_pic(self, article):

  146. pic = article('.weui_media_hd').attr('style')

  147.  
  148. p = re.compile(r'background-image:url\((.*?)\)')

  149. rs = p.findall(pic)

  150. self.log( '封面图片是:%s ' % rs[0] if len(rs) > 0 else '')

  151.  
  152. return rs[0] if len(rs) > 0 else ''

  153. #获取文章页面详情

  154. def parse_content_by_url(self, url):

  155. page_html = self.get_selenium_js_html(url)

  156. return pq(page_html)('#js_content')

  157.  
  158. #存储文章到本地

  159. def save_content_file(self,title,content):

  160. with open(title, 'w') as f:

  161. f.write(content)

  162.  
  163. #存贮json数据到本地

  164. def save_file(self, content):

  165. ' 数据写入文件 '

  166. with open(self.keywords+'/'+self.keywords+'.txt', 'w') as f:

  167. f.write(content)

  168. #自定义log函数,主要是加上时间

  169. def log(self, msg):

  170. print u'%s: %s' % (time.strftime('%Y-%m-%d %H:%M:%S'), msg)

  171. #验证函数

  172. def need_verify(self, selenium_html):

  173. ' 有时候对方会封锁ip,这里做一下判断,检测html中是否包含id=verify_change的标签,有的话,代表被重定向了,提醒过一阵子重试 '

  174. return pq(selenium_html)('#verify_change').text() != ''

  175. #创建公众号命名的文件夹

  176. def create_dir(self):

  177. if not os.path.exists(self.keywords):

  178. os.makedirs(self.keywords)

  179.  
  180. #爬虫主函数

  181. def run(self):

  182. ' 爬虫入口函数 '

  183. #Step 0 : 创建公众号命名的文件夹

  184. self.create_dir()

  185.  
  186. # Step 1:GET请求到搜狗微信引擎,以微信公众号英文名称作为查询关键字

  187. self.log(u'开始获取,微信公众号英文名为:%s' % self.keywords)

  188. self.log(u'开始调用sougou搜索引擎')

  189. sougou_search_html = self.get_search_result_by_keywords()

  190.  
  191. # Step 2:从搜索结果页中解析出公众号主页链接

  192. self.log(u'获取sougou_search_html成功,开始抓取公众号对应的主页wx_url')

  193. wx_url = self.get_wx_url_by_sougou_search_html(sougou_search_html)

  194. self.log(u'获取wx_url成功,%s' % wx_url)

  195.  
  196.  
  197. # Step 3:Selenium+PhantomJs获取js异步加载渲染后的html

  198. self.log(u'开始调用selenium渲染html')

  199. selenium_html = self.get_selenium_js_html(wx_url)

  200.  
  201. # Step 4: 检测目标网站是否进行了封锁

  202. if self.need_verify(selenium_html):

  203. self.log(u'爬虫被目标网站封锁,请稍后再试')

  204. else:

  205. # Step 5: 使用PyQuery,从Step 3获取的html中解析出公众号文章列表的数据

  206. self.log(u'调用selenium渲染html完成,开始解析公众号文章')

  207. articles = self.parse_wx_articles_by_html(selenium_html)

  208. self.log(u'抓取到微信文章%d篇' % len(articles))

  209.  
  210. # Step 6: 把微信文章数据封装成字典的list

  211. self.log(u'开始整合微信文章数据为字典')

  212. articles_list = self.switch_arctiles_to_list(articles)

  213.  
  214. # Step 7: 把Step 5的字典list转换为Json

  215. self.log(u'整合完成,开始转换为json')

  216. data_json = json.dumps(articles_list)

  217.  
  218. # Step 8: 写文件

  219. self.log(u'转换为json完成,开始保存json数据到文件')

  220. self.save_file(data_json)

  221.  
  222. self.log(u'保存完成,程序结束')

  223.  
  224. # main

  225. #几个可供参考的公众号

  226. #DataBureau

  227. #python6359

  228. #ArchNotes

  229. if __name__ == '__main__':

  230. print '''

  231. *****************************************

  232. ** Welcome to Spider of 公众号 **

  233. ** Created on 2017-04-12 **

  234. ** @author: Jimy _Fengqi **

  235. *****************************************

  236. '''

  237. gongzhonghao=raw_input(u'输入要爬取的公众号')

  238. if not gongzhonghao:

  239. gongzhonghao='python6359'

  240. weixin_spider(gongzhonghao).run()


 

四、总结

本文涉及到的知识点:

1.requests的get方法

2.爬虫header的设置

3.文件IO操作, 文件夹的创建,文件的创建

4.pyquery的使用,拓展内容beautifsoup的使用

5.phantomjs +webdriver的使用 ,  拓展  webdriver 中 xpath方法的使用

6.js 的加载

7.quote的使用

8. time 的使用   系统休眠和获取当前时间,处理时间格式

9.excel 的使用

本文使用的excel 相关包  pyExcelerator

from   pyExcelerator  import * #导入excel相关包

它的安装方法:

pip  install  pyExcelerator 

10.数据的存贮

11.正则表达式的使用

12.json的使用

13. 打印log 的变换方法

猜你喜欢

转载自blog.csdn.net/wufaliang003/article/details/81988981