导语
本文出自一个第一次接触python爬虫的新手,第一次独立写爬虫。所以很多东西都用的最基础的,没有使用成熟的python爬虫框架,只是请求网页,写正则表达式匹配需要信息。希望对新手的你有所帮助。
目标及思路
任务:
爬取国家统计局http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/index.html地理名称信息,包括省市区街道社区乡镇等。
思路:
该网站结构是树形的,即层层递进,想要爬取这里所有的信息,需要深度或广度搜索。这里,我采用了多叉树的层序遍历(不确定list容器是不是会爆掉)
结合目标网站进一步解释:
先获取当前网页根url,并将该url加入list容器。每次弹出一个url,进行相应加工成新的可点击的url,将新url压入list,同时通过正则匹配获取需要信息写入txt文件。重复操作直到list为空,证明所有的url都已经遍历完毕。
关键方法
层序遍历代码:(大致看看即可,因为每个人的目标网址和需要内容都不一样,主要是思想)
def bfsLink(LinkList,headers):
firstUrl = 'http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/'
f = codecs.open(r'f:/localInfo.txt', 'a','utf-8')
while(LinkList):
flag = 1 # 标记当前页面是爬到最低层还是非最低层,最低层的链接长度为17
addurl = LinkList.pop() # 当前页面超链接字符串
if(len(addurl)==7 or len(addurl)==12): # 11.html 11/1101.html
newurl = firstUrl + addurl
if(len(addurl)==14): # 01/110101.html
temp=addurl[3:5]
newurl=firstUrl+temp+"/"+addurl
if(len(addurl)==17): # 01/110101001.html
temp1=addurl[3:5]
temp2=addurl[5:7]
newurl=firstUrl+temp1+"/"+temp2+"/"+addurl
flag=0
# 非最低层的正则串和最低层正则串有略微不同
if(flag==1):
pattStr=ur'[0-9]{12}</a>.*?<td.*?><a.*?href=\'(.*?)\'>(.*?)</a></td>'
else:
pattStr=ur'[0-9]{3}</td>.*?</td><td>(.*?)</td></tr>'
print newurl
content=getPageContent(newurl,headers=headers)
pattern = re.compile(pattStr)
items = re.findall(pattern, content)
for item in items:
# 若该页面不是最低层,即存在超链接,则将超链接加入list
if flag==1:
str=getLabel(item[1])
print 'label:',str
f.write(item[1] + '@@' + str + '\r\n')
LinkList.append(item[0])
print item[1]
# 若该页面是最低层
else:
str = getLabel(item)
print 'label:', str
f.write(item + '@@' + str + '\r\n')
print item
f.close()
总结
常用代码
-
字符串保存成txt文件
content = u'你好,脚本之家 jb51.net' content+='\r\n' content+=u'你好,脚本之家 jb51.net' f = codecs.open('f:/1.txt','w','utf-8') # 重新写入文本,如果是追加,则‘w’改为‘a’即可 f.write(content) f.close()
- 爬虫的基本写法
# !/usr/bin/python
# -*- coding:utf-8 -*- #
import urllib2
import re
def getPageContent(url,headers):
request = urllib2.Request(url, headers=headers)
response = urllib2.urlopen(request)
content = response.read().decode('GB2312').encode('utf-8')
return content
url='http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/61/6101.html'
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
# 伪装成浏览器的访问
headers = {'User-Agent': user_agent}
content = getPageContent(url,headers)
# 输出网站内容
print content
# 正则串要根据爬取内容寻找规律,其中()代表一个分组,第一个括号即item[0],依次类推。
# 若只有一个分组,则直接循环输出item即可
patternStr=ur'[0-9]{12}</a>.*?<td.*?><a.*?href=\'(.*?)\'>(.*?)</a></td>'
pattern = re.compile(patternStr)
result = re.findall(pattern, content)
for item in result:
print item[0]
print item[1]
遇到的问题
-
问题:运行提示[Errno 10060]错误
原因:爬取页面过快造成暂时被网站禁止
解决方法一:在请求网页并获取content函数的代码中设置睡眠时间,防止爬取过快。(本来我设置的是睡眠1s,但还是报10060错误,偶然改成2s,只是中断的时间推迟了,还是没有爬完所有数据,最后,改成了睡眠时间是随机的,服务器更小概率地认为请求是非人类操作,竟然成功了)。代码为:second=random.uniform(2,5) # 每次生成一个2-5直接的随机浮点数 time.sleep(second)
解决方法二:设置代理。参看博客http://blog.csdn.net/wqy20140101/article/details/78547987 很多博主都说这样可以避免10060错误。
注:实际问题上,因为我这次爬取的数据实在是太多了,所以我采用了两者方法结合的方式,只设置了一个代理,并且加入了睡眠实际。即便我这样修改,还是总出现502等错误,所以中断的时候,我会人工检查在哪里中断,稍微改动代码接着运行,当然,这是很下策的方法,但是我实在没有找到其他的解决思路。 希望路过的朋友可以指点一二。 -
问题:报错sre_constants.error: unbalanced parenthesis
原因:正则表达式括号不匹配,例如:正则串最后一个右括号没有匹配对象pattStr=r'<td.*?class=.*?><td>(.*?)</td><td>(.*?)</td>)'
解决:反复检查修改正则串
-
问题:报错UnicodeDecodeError: ‘ascii’ codec can’t decode byte 0xe5 in position 1: ordinal not in range(128)
原因:读取文件时使用的编码默认时ascii而不是utf8
解决:加入以下代码import sys reload(sys) sys.setdefaultencoding('utf8')
-
中文乱码问题
原因:网站自身有编码,python程序默认有编码,两者编码很有可能是不一致的。若网站输出有中文,可能出现乱码问题
解决:首先查看网站自身的编码,在网页源码head标签中注明。
以实验网站为例:<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
因此,在读取页面时对网页内容先解码后编码。代码为:
request = urllib2.Request(url, headers=headers) response = urllib2.urlopen(request) content = response.read().decode('GB2312').encode('utf-8')