python爬虫初学(一)——基本代码和常见问题

时间:2021-08-21 05:30:34

导语

本文出自一个第一次接触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')