上学期参加了一个大数据比赛,需要抓取大量数据,于是我从新浪微博下手,本来准备使用新浪的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这两个模块。

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

代码:

  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()

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

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

  1. 【网络爬虫】【java】微博爬虫(一):小试牛刀——网易微博爬虫(自定义关键字爬取微博数据)(附软件源码)

    一.写在前面 (本专栏分为"java版微博爬虫"和"python版网络爬虫"两个项目,系列里所有文章将基于这两个项目讲解,项目完整源码已经整理到我的Github ...

  2. 一个月入门Python爬虫,轻松爬取大规模数据

    Python爬虫为什么受欢迎 如果你仔细观察,就不难发现,懂爬虫.学习爬虫的人越来越多,一方面,互联网可以获取的数据越来越多,另一方面,像 Python这样的编程语言提供越来越多的优秀工具,让爬虫变得 ...

  3. Python爬虫:如何爬取分页数据?

    上一篇文章<Python爬虫:爬取人人都是产品经理的数据>中说了爬取单页数据的方法,这篇文章详细解释如何爬取多页数据. 爬取对象: 有融网理财项目列表页[履约中]状态下的前10页数据,地址 ...

  4. 用python爬取微博数据并生成词云

    很早之前写过一篇怎么利用微博数据制作词云图片出来,之前的写得不完整,而且只能使用自己的数据,现在重新整理了一下,任何的微博数据都可以制作出来,放在今天应该比较应景. 一年一度的虐汪节,是继续蹲在角落默 ...

  5. 另类爬虫:从PDF文件中爬取表格数据

    简介   本文将展示一个稍微不一样点的爬虫.   以往我们的爬虫都是从网络上爬取数据,因为网页一般用HTML,CSS,JavaScript代码写成,因此,有大量成熟的技术来爬取网页中的各种数据.这次, ...

  6. 用Python爬取股票数据,绘制K线和均线并用机器学习预测股价(来自我出的书)

    最近我出了一本书,<基于股票大数据分析的Python入门实战 视频教学版>,京东链接:https://item.jd.com/69241653952.html,在其中用股票范例讲述Pyth ...

  7. 《Python网络编程》学习笔记--使用谷歌地理编码API获取一个JSON文档

    Foundations of Python Network Programing,Third Edition <python网络编程>,本书中的代码可在Github上搜索fopnp下载 本 ...

  8. python 网络编程 TCP/IP socket UDP

    TCP/IP简介 虽然大家现在对互联网很熟悉,但是计算机网络的出现比互联网要早很多. 计算机为了联网,就必须规定通信协议,早期的计算机网络,都是由各厂商自己规定一套协议,IBM.Apple和Micro ...

  9. 图解Python网络编程

    返回目录 本篇索引 (1)基本原理 (2)socket模块 (3)select模块 (4)asyncore模块 (5)asynchat模块 (6)socketserver模块 (1)基本原理 本篇指的 ...

随机推荐

  1. python合并2个字典

    2种方式,update()和items()方式 In [14]: a Out[14]: {'a': 1, 'b': 2, 'c': 3} In [15]: c = {'d': 4} In [16]: ...

  2. 软件产品案例分析--K米

    软件产品案例分析--K米 第一部分 调研,评测 评测 个人第一次上手体验 使用的第一款点歌软件,以为就是个遥控而已,使用后发现功能还挺多,能点挺久.觉得很方便,不用挤成一堆点歌了.K米的脸蛋(UI)好 ...

  3. POJ1185炮兵阵地(状态压缩 + dp)

    题目链接 题意:给出一张n * m的地图,其中 有的地方能放大炮,有的地方不能,大炮与上下左右两个单位范围内会相互攻击,问最多能放几个大炮 能放大炮为1不能放大炮为0,把每一行看做一个状态,要除去同一 ...

  4. 为什么springMVC和Mybatis逐渐流行起来了?

    http://www.zhihu.com/question/36032573 https://github.com/bingoohuang/eql

  5. MYSQL 常用函数(数学、字符串、日期时间、系统信息、加密)

    一.数学函数 数学函数主要用于处理数字,包括整型.浮点数等. ABS(x) 返回x的绝对值 SELECT ABS(-1) -- 返回1 CEIL(x),CEILING(x) 返回大于或等于x的最小整数 ...

  6. webstorm9.3 安装less 编译css教程

    第一步:安装node.js webstrom9.3汉化下载:http://pan.baidu.com/s/1ntGNNiL    密码:1fbh node.js 下载地址:https://nodejs ...

  7. easyUI Form表单的密码验证是否相同

    一.js文件中的代码: $(function(){ $.extend($.fn.validatebox.defaults.rules, { equals: {//定义一个比较相等与否的函数 valid ...

  8. CSS学习笔记——选择器

    选择器 当我们定义一条样式规则时候,这条样式规则会作用于网页当中的某些元素,而我们的规定的这些元素的规则就叫做选择器. 常用的选择器: 1.id选择器 #idname 2.类选择器 .classnam ...

  9. css 父层 透明 子层不透明Alpha

    html代码 <div class="user2-register-bg"> <div class="user2-register-con"& ...

  10. autofac Adding services after container has been built

    http://stackoverflow.com/questions/6173566/run-time-registration-with-autofac Yes you can, using the ...