【python网络编程】新浪爬虫:关键词搜索爬取微博数据
上学期参加了一个大数据比赛,需要抓取大量数据,于是我从新浪微博下手,本来准备使用新浪的API的,无奈新浪并没有开放关键字搜索的API,所以只能用爬虫来获取了。幸运的是,新浪提供了一个高级搜索功能,为我们爬取数据提供了一个很好的切入点。
在查阅了一些资料,参考了一些爬虫的例子后,得到大体思路:构造URL,爬取网页,然后解析网页
具体往下看~
登陆新浪微博,进入高级搜索,如图输入,之后发送请求会发现地址栏变为如下: http://s.weibo.com/weibo/%25E4%25B8%25AD%25E5%25B1%25B1%25E5%25A4%25A7%25E5%25AD%25A6®ion=custom:44:1&typeall=1&suball=1×cope=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这两个模块。
最后数据的效果(我搜集的信息比较具体,需要访问博主的个人主页获取,为便于大家阅读、理解,下面代码中删去了这部分):
代码:
- # coding: utf-8
- '''''
- 以关键词收集新浪微博
- '''
- import wx
- import sys
- import urllib
- import urllib2
- import re
- import json
- import hashlib
- import os
- import time
- from datetime import datetime
- from datetime import timedelta
- import random
- from lxml import etree
- import logging
- import xlwt
- import xlrd
- from xlutils.copy import copy
- class CollectData():
- """数据收集类
- 利用微博高级搜索功能,按关键字搜集一定时间范围内的微博。
- """
- def __init__(self, keyword, startTime, interval='50', flag=True, begin_url_per = "http://s.weibo.com/weibo/"):
- self.begin_url_per = begin_url_per #设置固定地址部分
- self.setKeyword(keyword) #设置关键字
- self.setStartTimescope(startTime) #设置搜索的开始时间
- #self.setRegion(region) #设置搜索区域
- self.setInterval(interval) #设置邻近网页请求之间的基础时间间隔(注意:过于频繁会被认为是机器人)
- self.setFlag(flag)
- self.logger = logging.getLogger('main.CollectData') #初始化日志
- ##设置关键字
- ##关键字需解码后编码为utf-8
- def setKeyword(self, keyword):
- self.keyword = keyword.decode('GBK','ignore').encode("utf-8")
- print 'twice encode:',self.getKeyWord()
- ##关键字需要进行两次urlencode
- def getKeyWord(self):
- once = urllib.urlencode({"kw":self.keyword})[3:]
- return urllib.urlencode({"kw":once})[3:]
- ##设置起始范围,间隔为1天
- ##格式为:yyyy-mm-dd
- def setStartTimescope(self, startTime):
- if not (startTime == '-'):
- self.timescope = startTime + ":" + startTime
- else:
- self.timescope = '-'
- ##设置搜索地区
- #def setRegion(self, region):
- # self.region = region
- ##设置邻近网页请求之间的基础时间间隔
- def setInterval(self, interval):
- self.interval = int(interval)
- ##设置是否被认为机器人的标志。若为False,需要进入页面,手动输入验证码
- def setFlag(self, flag):
- self.flag = flag
- ##构建URL
- def getURL(self):
- return self.begin_url_per+self.getKeyWord()+"&typeall=1&suball=1×cope=custom:"+self.timescope+"&page="
- ##爬取一次请求中的所有网页,最多返回50页
- def download(self, url, maxTryNum=4):
- hasMore = True #某次请求可能少于50页,设置标记,判断是否还有下一页
- isCaught = False #某次请求被认为是机器人,设置标记,判断是否被抓住。抓住后,需要,进入页面,输入验证码
- name_filter = set([]) #过滤重复的微博ID
- i = 1 #记录本次请求所返回的页数
- while hasMore and i < 51 and (not isCaught): #最多返回50页,对每页进行解析,并写入结果文件
- source_url = url + str(i) #构建某页的URL
- data = '' #存储该页的网页数据
- goon = True #网络中断标记
- ##网络不好的情况,试着尝试请求三次
- for tryNum in range(maxTryNum):
- try:
- html = urllib2.urlopen(source_url, timeout=12)
- data = html.read()
- break
- except:
- if tryNum < (maxTryNum-1):
- time.sleep(10)
- else:
- print 'Internet Connect Error!'
- self.logger.error('Internet Connect Error!')
- self.logger.info('url: ' + source_url)
- self.logger.info('fileNum: ' + str(fileNum))
- self.logger.info('page: ' + str(i))
- self.flag = False
- goon = False
- break
- if goon:
- lines = data.splitlines()
- isCaught = True
- for line in lines:
- ## 判断是否有微博内容,出现这一行,则说明没有被认为是机器人
- if line.startswith('<script>STK && STK.pageletM && STK.pageletM.view({"pid":"pl_weibo_direct"'):
- isCaught = False
- n = line.find('html":"')
- if n > 0:
- j = line[n + 7: -12].encode("utf-8").decode('unicode_escape').encode("utf-8").replace("\\", "") #去掉所有的\
- ## 没有更多结果页面
- if (j.find('<div class="search_noresult">') > 0):
- hasMore = False
- ## 有结果的页面
- else:
- #此处j要decode,因为上面j被encode成utf-8了
- page = etree.HTML(j.decode('utf-8'))
- ps = page.xpath("//p[@node-type='feed_list_content']") #使用xpath解析得到微博内容
- addrs = page.xpath("//a[@class='W_texta W_fb']") #使用xpath解析得到博主地址
- addri = 0
- #获取昵称和微博内容
- for p in ps:
- name = p.attrib.get('nick-name') #获取昵称
- txt = p.xpath('string(.)') #获取微博内容
- addr = addrs[addri].attrib.get('href') #获取微博地址
- addri += 1
- if(name != 'None' and str(txt) != 'None' and name not in name_filter): #导出数据到excel中
- name_filter.add(name)
- oldWb = xlrd.open_workbook('weiboData.xls', formatting_info=True)
- oldWs = oldWb.sheet_by_index(0)
- rows = int(oldWs.cell(0,0).value)
- newWb = copy(oldWb)
- newWs = newWb.get_sheet(0)
- newWs.write(rows, 0, str(rows))
- newWs.write(rows, 1, name)
- newWs.write(rows, 2, self.timescope)
- newWs.write(rows, 3, addr)
- newWs.write(rows, 4, txt)
- newWs.write(0, 0, str(rows+1))
- newWb.save('weiboData.xls')
- print "save with same name ok"
- break
- lines = None
- ## 处理被认为是机器人的情况
- if isCaught:
- print 'Be Caught!'
- self.logger.error('Be Caught Error!')
- self.logger.info('filePath: ' + savedir)
- self.logger.info('url: ' + source_url)
- self.logger.info('fileNum: ' + str(fileNum))
- self.logger.info('page:' + str(i))
- data = None
- self.flag = False
- break
- ## 没有更多结果,结束该次请求,跳到下一个请求
- if not hasMore:
- print 'No More Results!'
- if i == 1:
- time.sleep(random.randint(3,8))
- else:
- time.sleep(10)
- data = None
- break
- i += 1
- ## 设置两个邻近URL请求之间的随机休眠时间,防止Be Caught
- sleeptime_one = random.randint(self.interval-25,self.interval-15)
- sleeptime_two = random.randint(self.interval-15,self.interval)
- if i%2 == 0:
- sleeptime = sleeptime_two
- else:
- sleeptime = sleeptime_one
- print 'sleeping ' + str(sleeptime) + ' seconds...'
- time.sleep(sleeptime)
- else:
- break
- ##改变搜索的时间范围,有利于获取最多的数据
- def getTimescope(self, perTimescope):
- if not (perTimescope=='-'):
- times_list = perTimescope.split(':')
- start_date = datetime(int(times_list[-1][0:4]), int(times_list[-1][5:7]), int(times_list[-1][8:10]) )
- start_new_date = start_date + timedelta(days = 1)
- start_str = start_new_date.strftime("%Y-%m-%d")
- return start_str + ":" + start_str
- else:
- return '-'
- def main():
- logger = logging.getLogger('main')
- logFile = './collect.log'
- logger.setLevel(logging.DEBUG)
- filehandler = logging.FileHandler(logFile)
- formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s: %(message)s')
- filehandler.setFormatter(formatter)
- logger.addHandler(filehandler)
- while True:
- ## 接受键盘输入
- keyword = raw_input('Enter the keyword(type \'quit\' to exit ):')
- if keyword == 'quit':
- sys.exit()
- startTime = raw_input('Enter the start time(Format:YYYY-mm-dd):')
- #region = raw_input('Enter the region([BJ]11:1000,[SH]31:1000,[GZ]44:1,[CD]51:1):')
- interval = raw_input('Enter the time interval( >30 and deafult:50):')
- ##实例化收集类,收集指定关键字和起始时间的微博
- cd = CollectData(keyword, startTime, interval)
- while cd.flag:
- print cd.timescope
- logger.info(cd.timescope)
- url = cd.getURL()
- cd.download(url)
- cd.timescope = cd.getTimescope(cd.timescope) #改变搜索的时间,到下一天
- else:
- cd = None
- print '-----------------------------------------------------'
- print '-----------------------------------------------------'
- else:
- logger.removeHandler(filehandler)
- logger = None
- ##if __name__ == '__main__':
- ## main()
上面实现了数据的爬取,再结合上一篇文章中的模拟登录,就可以美美的抓数据啦~
【python网络编程】新浪爬虫:关键词搜索爬取微博数据的更多相关文章
- 【网络爬虫】【java】微博爬虫(一):小试牛刀——网易微博爬虫(自定义关键字爬取微博数据)(附软件源码)
一.写在前面 (本专栏分为"java版微博爬虫"和"python版网络爬虫"两个项目,系列里所有文章将基于这两个项目讲解,项目完整源码已经整理到我的Github ...
- 一个月入门Python爬虫,轻松爬取大规模数据
Python爬虫为什么受欢迎 如果你仔细观察,就不难发现,懂爬虫.学习爬虫的人越来越多,一方面,互联网可以获取的数据越来越多,另一方面,像 Python这样的编程语言提供越来越多的优秀工具,让爬虫变得 ...
- Python爬虫:如何爬取分页数据?
上一篇文章<Python爬虫:爬取人人都是产品经理的数据>中说了爬取单页数据的方法,这篇文章详细解释如何爬取多页数据. 爬取对象: 有融网理财项目列表页[履约中]状态下的前10页数据,地址 ...
- 用python爬取微博数据并生成词云
很早之前写过一篇怎么利用微博数据制作词云图片出来,之前的写得不完整,而且只能使用自己的数据,现在重新整理了一下,任何的微博数据都可以制作出来,放在今天应该比较应景. 一年一度的虐汪节,是继续蹲在角落默 ...
- 另类爬虫:从PDF文件中爬取表格数据
简介 本文将展示一个稍微不一样点的爬虫. 以往我们的爬虫都是从网络上爬取数据,因为网页一般用HTML,CSS,JavaScript代码写成,因此,有大量成熟的技术来爬取网页中的各种数据.这次, ...
- 用Python爬取股票数据,绘制K线和均线并用机器学习预测股价(来自我出的书)
最近我出了一本书,<基于股票大数据分析的Python入门实战 视频教学版>,京东链接:https://item.jd.com/69241653952.html,在其中用股票范例讲述Pyth ...
- 《Python网络编程》学习笔记--使用谷歌地理编码API获取一个JSON文档
Foundations of Python Network Programing,Third Edition <python网络编程>,本书中的代码可在Github上搜索fopnp下载 本 ...
- python 网络编程 TCP/IP socket UDP
TCP/IP简介 虽然大家现在对互联网很熟悉,但是计算机网络的出现比互联网要早很多. 计算机为了联网,就必须规定通信协议,早期的计算机网络,都是由各厂商自己规定一套协议,IBM.Apple和Micro ...
- 图解Python网络编程
返回目录 本篇索引 (1)基本原理 (2)socket模块 (3)select模块 (4)asyncore模块 (5)asynchat模块 (6)socketserver模块 (1)基本原理 本篇指的 ...
随机推荐
- python合并2个字典
2种方式,update()和items()方式 In [14]: a Out[14]: {'a': 1, 'b': 2, 'c': 3} In [15]: c = {'d': 4} In [16]: ...
- 软件产品案例分析--K米
软件产品案例分析--K米 第一部分 调研,评测 评测 个人第一次上手体验 使用的第一款点歌软件,以为就是个遥控而已,使用后发现功能还挺多,能点挺久.觉得很方便,不用挤成一堆点歌了.K米的脸蛋(UI)好 ...
- POJ1185炮兵阵地(状态压缩 + dp)
题目链接 题意:给出一张n * m的地图,其中 有的地方能放大炮,有的地方不能,大炮与上下左右两个单位范围内会相互攻击,问最多能放几个大炮 能放大炮为1不能放大炮为0,把每一行看做一个状态,要除去同一 ...
- 为什么springMVC和Mybatis逐渐流行起来了?
http://www.zhihu.com/question/36032573 https://github.com/bingoohuang/eql
- MYSQL 常用函数(数学、字符串、日期时间、系统信息、加密)
一.数学函数 数学函数主要用于处理数字,包括整型.浮点数等. ABS(x) 返回x的绝对值 SELECT ABS(-1) -- 返回1 CEIL(x),CEILING(x) 返回大于或等于x的最小整数 ...
- webstorm9.3 安装less 编译css教程
第一步:安装node.js webstrom9.3汉化下载:http://pan.baidu.com/s/1ntGNNiL 密码:1fbh node.js 下载地址:https://nodejs ...
- easyUI Form表单的密码验证是否相同
一.js文件中的代码: $(function(){ $.extend($.fn.validatebox.defaults.rules, { equals: {//定义一个比较相等与否的函数 valid ...
- CSS学习笔记——选择器
选择器 当我们定义一条样式规则时候,这条样式规则会作用于网页当中的某些元素,而我们的规定的这些元素的规则就叫做选择器. 常用的选择器: 1.id选择器 #idname 2.类选择器 .classnam ...
- css 父层 透明 子层不透明Alpha
html代码 <div class="user2-register-bg"> <div class="user2-register-con"& ...
- autofac Adding services after container has been built
http://stackoverflow.com/questions/6173566/run-time-registration-with-autofac Yes you can, using the ...