【python网络编程】新浪爬虫:关键词搜索爬取微博数据

上学期参加了一个大数据比赛,需要抓取大量数据,于是我从新浪微博下手,本来准备使用新浪的API的,无奈新浪并没有开放关键字搜索的API,所以只能用爬虫来获取了。幸运的是,新浪提供了一个高级搜索功能,为我们爬取数据提供了一个很好的切入点。

在查阅了一些资料,参考了一些爬虫的例子后,得到大体思路:构造URL,爬取网页,然后解析网页

具体往下看~

登陆新浪微博,进入高级搜索,如图输入,之后发送请求会发现地址栏变为如下:    http://s.weibo.com/weibo/%25E4%25B8%25AD%25E5%25B1%25B1%25E5%25A4%25A7%25E5%25AD%25A6&region=custom:44:1&typeall=1&suball=1&timescope=custom:2015-08-07-0:2015-08-08-0&Refer=g

解析如下:
            固定地址部分:http://s.weibo.com/weibo/
            关键字二次UTF-8编码:%25E4%25B8%25AD%25E5%25B1%25B1%25E5%25A4%25A7%25E5%25AD%25A6
            搜索地区:region=custom:44:1
            搜索时间范围:timescope=custom:2015-08-07-0:2015-08-08-0
            可忽略项:Refer=g
            某次请求的页数:page=1(第一页可不加)

我们查看一下网页源代码看看有什么鬼:

小伙伴们第一次看到肯定大呼我的天啊,真的是看的眼花缭乱。

别着急,让我娓娓道来。

首先,我们定位到图示的地方,即出现字符串<script>STK && STK.pageletM && STK.pageletM.view({"pid":"pl_weibo_direct"的地方,此处即搜索到的微博页面的代码啦~
    页面是unicode码,所以中文都不能正常显示~而且上面没有排版,才显得如此杂乱。

我们可以先对抓取到的页面处理一下,这时就要用到lxml的etree了,它可以将网页内容的结点构建成一棵树。

我们拿出其中一个结点出来看看:

<a class=\"W_texta W_fb\" nick-name=\"\u554a\u5be7\u5504\" href=\"http:\/\/weibo.com\/612364698\" target=\"_blank\" title=\"\u554a\u5be7\u5504\" usercard=\"id=1884932730&usercardkey=weibo_mp\"\t\tsuda-data=\"key=tblog_search_weibo&value=weibo_ss_1_name\" class=\"name_txt W_fb\">

在这个结点中,我们可以获取该条微博的博主的一些信息,如nick-name,微博地址href。

我们再看看另一个结点:

<p class=\"comment_txt\" node-type=\"feed_list_content\" nick-name=\"\u554a\u5be7\u5504\">\u8fd9\u4e48\u52aa\u529b \u5c45\u7136\u5012\u6570\u7b2c\u4e94 \u5509 \u4e0d\u884c\u6211\u8981\u8ffd\u56de\u6765 \u8d8a\u632b\u8d8a\u52c7 \u4e0d\u53ef\u4ee5\u81ea\u66b4\u81ea\u5f03 \u4e0d\u53ef\u4ee5\u8ba9\u8d1f\u9762\u60c5\u7eea\u8dd1\u51fa\u6765 \u83dc\u575a\u5f3a \u52a0\u6cb9\u52a0\u6cb9\u52a0\u6cb9 \u6211\u8981\u4e0a<em class=\"red\">\u4e2d\u5c71\u5927\u5b66<\/em> \u6211\u8981\u548c\u5c0f\u54c8\u5427\u4e00\u6240\u5927\u5b66 \u62fc\u4e86<\/p>

这个结点包含的数据即为微博的内容。

这样子就清晰很多了。至于如何搜索相应的结点,取得结点的属性和内容等,我们用的是xpath这个工具。

关于xpath,见文 http://blog.csdn.net/raptor/article/details/4516441

获得数据后,是数据的保存,我是将数据导入到excel中,用到的xlwt和xlrd这两个模块。

最后数据的效果(我搜集的信息比较具体,需要访问博主的个人主页获取,为便于大家阅读、理解,下面代码中删去了这部分):

代码:

[python] view plain copy

  1. # coding: utf-8
  2. ‘‘‘‘‘
  3. 以关键词收集新浪微博
  4. ‘‘‘
  5. import wx
  6. import sys
  7. import urllib
  8. import urllib2
  9. import re
  10. import json
  11. import hashlib
  12. import os
  13. import time
  14. from datetime import datetime
  15. from datetime import timedelta
  16. import random
  17. from lxml import etree
  18. import logging
  19. import xlwt
  20. import xlrd
  21. from xlutils.copy import copy
  22. class CollectData():
  23. """数据收集类
  24. 利用微博高级搜索功能,按关键字搜集一定时间范围内的微博。
  25. """
  26. def __init__(self, keyword, startTime, interval=‘50‘, flag=True, begin_url_per = "http://s.weibo.com/weibo/"):
  27. self.begin_url_per = begin_url_per  #设置固定地址部分
  28. self.setKeyword(keyword)    #设置关键字
  29. self.setStartTimescope(startTime)   #设置搜索的开始时间
  30. #self.setRegion(region)  #设置搜索区域
  31. self.setInterval(interval)  #设置邻近网页请求之间的基础时间间隔(注意:过于频繁会被认为是机器人)
  32. self.setFlag(flag)
  33. self.logger = logging.getLogger(‘main.CollectData‘) #初始化日志
  34. ##设置关键字
  35. ##关键字需解码后编码为utf-8
  36. def setKeyword(self, keyword):
  37. self.keyword = keyword.decode(‘GBK‘,‘ignore‘).encode("utf-8")
  38. print ‘twice encode:‘,self.getKeyWord()
  39. ##关键字需要进行两次urlencode
  40. def getKeyWord(self):
  41. once = urllib.urlencode({"kw":self.keyword})[3:]
  42. return urllib.urlencode({"kw":once})[3:]
  43. ##设置起始范围,间隔为1天
  44. ##格式为:yyyy-mm-dd
  45. def setStartTimescope(self, startTime):
  46. if not (startTime == ‘-‘):
  47. self.timescope = startTime + ":" + startTime
  48. else:
  49. self.timescope = ‘-‘
  50. ##设置搜索地区
  51. #def setRegion(self, region):
  52. #    self.region = region
  53. ##设置邻近网页请求之间的基础时间间隔
  54. def setInterval(self, interval):
  55. self.interval = int(interval)
  56. ##设置是否被认为机器人的标志。若为False,需要进入页面,手动输入验证码
  57. def setFlag(self, flag):
  58. self.flag = flag
  59. ##构建URL
  60. def getURL(self):
  61. return self.begin_url_per+self.getKeyWord()+"&typeall=1&suball=1×cope=custom:"+self.timescope+"&page="
  62. ##爬取一次请求中的所有网页,最多返回50页
  63. def download(self, url, maxTryNum=4):
  64. hasMore = True  #某次请求可能少于50页,设置标记,判断是否还有下一页
  65. isCaught = False    #某次请求被认为是机器人,设置标记,判断是否被抓住。抓住后,需要,进入页面,输入验证码
  66. name_filter = set([])    #过滤重复的微博ID
  67. i = 1   #记录本次请求所返回的页数
  68. while hasMore and i < 51 and (not isCaught):    #最多返回50页,对每页进行解析,并写入结果文件
  69. source_url = url + str(i)   #构建某页的URL
  70. data = ‘‘   #存储该页的网页数据
  71. goon = True #网络中断标记
  72. ##网络不好的情况,试着尝试请求三次
  73. for tryNum in range(maxTryNum):
  74. try:
  75. html = urllib2.urlopen(source_url, timeout=12)
  76. data = html.read()
  77. break
  78. except:
  79. if tryNum < (maxTryNum-1):
  80. time.sleep(10)
  81. else:
  82. print ‘Internet Connect Error!‘
  83. self.logger.error(‘Internet Connect Error!‘)
  84. self.logger.info(‘url: ‘ + source_url)
  85. self.logger.info(‘fileNum: ‘ + str(fileNum))
  86. self.logger.info(‘page: ‘ + str(i))
  87. self.flag = False
  88. goon = False
  89. break
  90. if goon:
  91. lines = data.splitlines()
  92. isCaught = True
  93. for line in lines:
  94. ## 判断是否有微博内容,出现这一行,则说明没有被认为是机器人
  95. if line.startswith(‘<script>STK && STK.pageletM && STK.pageletM.view({"pid":"pl_weibo_direct"‘):
  96. isCaught = False
  97. n = line.find(‘html":"‘)
  98. if n > 0:
  99. j = line[n + 7: -12].encode("utf-8").decode(‘unicode_escape‘).encode("utf-8").replace("\\", "")    #去掉所有的\
  100. ## 没有更多结果页面
  101. if (j.find(‘<div class="search_noresult">‘) > 0):
  102. hasMore = False
  103. ## 有结果的页面
  104. else:
  105. #此处j要decode,因为上面j被encode成utf-8了
  106. page = etree.HTML(j.decode(‘utf-8‘))
  107. ps = page.xpath("//p[@node-type=‘feed_list_content‘]")   #使用xpath解析得到微博内容
  108. addrs = page.xpath("//a[@class=‘W_texta W_fb‘]")   #使用xpath解析得到博主地址
  109. addri = 0
  110. #获取昵称和微博内容
  111. for p in ps:
  112. name = p.attrib.get(‘nick-name‘)    #获取昵称
  113. txt = p.xpath(‘string(.)‘)          #获取微博内容
  114. addr = addrs[addri].attrib.get(‘href‘)  #获取微博地址
  115. addri += 1
  116. if(name != ‘None‘ and str(txt) != ‘None‘ and name not in name_filter):  #导出数据到excel中
  117. name_filter.add(name)
  118. oldWb = xlrd.open_workbook(‘weiboData.xls‘, formatting_info=True)
  119. oldWs = oldWb.sheet_by_index(0)
  120. rows = int(oldWs.cell(0,0).value)
  121. newWb = copy(oldWb)
  122. newWs = newWb.get_sheet(0)
  123. newWs.write(rows, 0, str(rows))
  124. newWs.write(rows, 1, name)
  125. newWs.write(rows, 2, self.timescope)
  126. newWs.write(rows, 3, addr)
  127. newWs.write(rows, 4, txt)
  128. newWs.write(0, 0, str(rows+1))
  129. newWb.save(‘weiboData.xls‘)
  130. print "save with same name ok"
  131. break
  132. lines = None
  133. ## 处理被认为是机器人的情况
  134. if isCaught:
  135. print ‘Be Caught!‘
  136. self.logger.error(‘Be Caught Error!‘)
  137. self.logger.info(‘filePath: ‘ + savedir)
  138. self.logger.info(‘url: ‘ + source_url)
  139. self.logger.info(‘fileNum: ‘ + str(fileNum))
  140. self.logger.info(‘page:‘ + str(i))
  141. data = None
  142. self.flag = False
  143. break
  144. ## 没有更多结果,结束该次请求,跳到下一个请求
  145. if not hasMore:
  146. print ‘No More Results!‘
  147. if i == 1:
  148. time.sleep(random.randint(3,8))
  149. else:
  150. time.sleep(10)
  151. data = None
  152. break
  153. i += 1
  154. ## 设置两个邻近URL请求之间的随机休眠时间,防止Be Caught
  155. sleeptime_one = random.randint(self.interval-25,self.interval-15)
  156. sleeptime_two = random.randint(self.interval-15,self.interval)
  157. if i%2 == 0:
  158. sleeptime = sleeptime_two
  159. else:
  160. sleeptime = sleeptime_one
  161. print ‘sleeping ‘ + str(sleeptime) + ‘ seconds...‘
  162. time.sleep(sleeptime)
  163. else:
  164. break
  165. ##改变搜索的时间范围,有利于获取最多的数据
  166. def getTimescope(self, perTimescope):
  167. if not (perTimescope==‘-‘):
  168. times_list = perTimescope.split(‘:‘)
  169. start_date =  datetime(int(times_list[-1][0:4]),  int(times_list[-1][5:7]), int(times_list[-1][8:10]) )
  170. start_new_date = start_date + timedelta(days = 1)
  171. start_str = start_new_date.strftime("%Y-%m-%d")
  172. return start_str + ":" + start_str
  173. else:
  174. return ‘-‘
  175. def main():
  176. logger = logging.getLogger(‘main‘)
  177. logFile = ‘./collect.log‘
  178. logger.setLevel(logging.DEBUG)
  179. filehandler = logging.FileHandler(logFile)
  180. formatter = logging.Formatter(‘%(asctime)s - %(name)s - %(levelname)s: %(message)s‘)
  181. filehandler.setFormatter(formatter)
  182. logger.addHandler(filehandler)
  183. while True:
  184. ## 接受键盘输入
  185. keyword = raw_input(‘Enter the keyword(type \‘quit\‘ to exit ):‘)
  186. if keyword == ‘quit‘:
  187. sys.exit()
  188. startTime = raw_input(‘Enter the start time(Format:YYYY-mm-dd):‘)
  189. #region = raw_input(‘Enter the region([BJ]11:1000,[SH]31:1000,[GZ]44:1,[CD]51:1):‘)
  190. interval = raw_input(‘Enter the time interval( >30 and deafult:50):‘)
  191. ##实例化收集类,收集指定关键字和起始时间的微博
  192. cd = CollectData(keyword, startTime, interval)
  193. while cd.flag:
  194. print cd.timescope
  195. logger.info(cd.timescope)
  196. url = cd.getURL()
  197. cd.download(url)
  198. cd.timescope = cd.getTimescope(cd.timescope)  #改变搜索的时间,到下一天
  199. else:
  200. cd = None
  201. print ‘-----------------------------------------------------‘
  202. print ‘-----------------------------------------------------‘
  203. else:
  204. logger.removeHandler(filehandler)
  205. logger = None
  206. ##if __name__ == ‘__main__‘:
  207. ##    main()

上面实现了数据的爬取,再结合上一篇文章中的模拟登录,就可以美美的抓数据啦~

时间: 2024-10-19 17:49:49

【python网络编程】新浪爬虫:关键词搜索爬取微博数据的相关文章

爬虫实例(一)——爬取微博动态

首语:开始准备认真学习爬虫了,先从基础的开始学起,比如先爬取微博的个人动态. 两个难点:获取动态加载的内容和翻页这两项操作. 对象:何炅的个人 需要的URL: 首页url:https://weibo.com/hejiong?is_search=0&visible=0&is_all=1&is_tag=0&profile_ftype=1&page=1#feedtop 我们可以直接用get方法请求该URL,但是注意要带上cookies,这样才能得到网页信息.cookies

爬虫实例 利用Ajax爬取微博数据

随着代理IP技术的普及,爬虫的使用也变得简单起来,许多企业和个人都开始用爬虫技术来抓取数据.那么今天就来分享一个爬虫实例,帮助你们更好的理解爬虫.下面我们用程序模拟Ajax请求,将我的前10页微博全部爬取下来.首先,定义一个方法来获取每次请求的结果.在请求时,page是一个可变参数,所以我们将它作为方法的参数传递进来,相关代码如下:首先,这里定义了base_url来表示请求的URL的前半部分.接下来,构造参数字典,其中type.value和containerid是固定参数,page是可变参数.接

爬虫——使用多进程爬取视频数据

以梨视频为例分析页面请求抓取网页数据.本次抓取梨视频生活分类页面下的部分视频数据,并保存到本地. 一.分析网页 打开抓取网页,查看网页代码结构,发现网页结构里面存放视频的地址并不是真正的视频地址. 进入视频详情页面查看后,可以在response中找到真正的视频地址.保存这个地址的并不是标签,而是一个变量,我们使用re来解析这个变量,提取信息. 二.代码实现 """使用多线程爬取梨视频视频数据""" import requests import r

[Python网络编程]gevent httpclient以及网页编码

之前看到geventhttpclient这个项目,https://github.com/gwik/geventhttpclient,官方文档说非常快,由于响应使用了C的解析,所以我一直想把这玩意用到项目中, 这两天一直在纠结这玩意,说实在一句话,比较难用,封装的不给力,最大缺陷如下: 1.不支持重定向,重定向需要自己来写,很费事 2.新建的httpclient对象只能发送同域名的请求 这相当的蛋疼,我花了一点时间封装了一下,解决了上面的两个问题,还增加了自动编解码问题,代码如下: #!/usr/

Python网络编程08----Django模版

模板系统基本知识 模板是一个文本文件(可以是HTML,XML,CSV等任何文本格式),同时包含了静态内容(例如HTML)和动态标记的逻辑,用于分离文档的表现形式和内容. 模板定义了占位符以及各种用于规范文档该如何显示的各部分基本逻辑(模板标签). 模板通常用于产生HTML,但是Django的模板也能产生任何基于文本格式的文档.使用哪个模版以及渲染什么数据是由视图函数本身(通过显式的渲染或者使用render_to_response)或者视图的参数(比如通用视图里的template_name参数)决

[Python网络编程] DNS缓存解决方案

记得以前写爬虫的时候为了防止dns多次查询,是直接修改/etc/hosts文件的,最近看到一个优美的解决方案,修改后记录如下: import socket _dnscache={} def _setDNSCache(): """ Makes a cached version of socket._getaddrinfo to avoid subsequent DNS requests. """ def _getaddrinfo(*args, **

[python] 网络编程之套接字Socket、TCP和UDP通信实例

很早以前研究过C#和C++的网络通信,参考我的文章: C#网络编程之Tcp实现客户端和服务器聊天 C#网络编程之套接字编程基础知识 C#网络编程之使用Socket类Send.Receive方法的同步通讯 Python网络编程也类似.同时最近找工作笔试面试考察Socket套接字.TCP\UDP区别比较多,所以这篇文章主要精简了<Python核心编程(第二版)>第16章内容.内容包括:服务器和客户端架构.套接字Socket.TCP\UDP通信实例和常见笔试考题. 最后希望文章对你有所帮助,如果有不

Python 网络编程(一)

Python 网络编程 socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. 百分号的方式相对来说比较老,而format方式则是比较先进的方式,企图替换古老的方式,目前两者. socket和file的区别: file模块是针对某个指定文件进行[打开][读写][关闭] socket模块是针对 服务器端 和 客户端Socket 进行[打开][读写][关闭] socket服务端和客户端的网

[Python网络编程]浅析守护进程后台任务的设计与实现

在做基于B/S应用中,经常有需要后台运行任务的需求,最简单比如发送邮件.在一些如防火墙,WAF等项目中,前台只是为了展示内容与各种参数配置,后台守护进程才是重头戏.所以在防火墙配置页面中可能会经常看到调用cgi,但真正做事的一般并不是cgi,比如说执行关机命令,他们的逻辑如下: (ps:上图所说的前台界面包含通常web开发中的后端,不然也没有socket一说) 为什么要这么设计 你可能疑惑为什么要这么设计,我觉得理由如下: 首先有一点说明,像防火墙等基本上都运行在类Linux平台上 1.安全问题