零基础简单爬虫制作(以wjyt-china企业黄页爬虫为例)(下)

六、urllib2
推荐学习文章合集《如何学习Python爬虫[入门篇]》https://zhuanlan.zhihu.com/p/21479334。该合集中整理的《Python爬虫入门三之Urllib库的基本使用》http://cuiqingcai.com/947.html更完整地介绍了urllib2库的入门知识。此合集的内容全面,由浅入深。Python正则表达式部分推荐的博客也来自此合集。
在本教程的开始,我们提到过制作一个爬虫并使用爬虫工作的流程大致为:
1、分析网页的HTML与JavaScript,了解目标信息在网页源代码中的位置。
2、通过爬虫抓取网页源代码。
3、通过Python处理源代码,获取所需信息。
我们已经在前几节完成了第一步和第三步。苦尽甘来,剩下的第二步事实上是最简单的。在进行复杂的爬取任务时,我们当然需要强大的爬虫库及框架。但是通常,我们只需要几行代码就可以完成爬取网页源代码的操作。几行代码当然只能形成一个简单的爬虫,但是大多数网页也仅仅是简单的网页。这就是为什么Python内置的urllib2库常常就已足够的原因。
下面是一个通过urllib2库爬取网页源代码的经典范式。

import urllib2#载入urllib2库

url = r'exampleWebsite'#将网址以原生字符串的形式赋给url变量
request = urllib2.Request(url)#调用urllib2库方法构造一个request对象,用于发送请求
response = urllib2.urlopen(request)
#发送请求并将网页的应答(也即网页的HTML文件,网页的源代码)储存在response对象中
print response.read()#通过read()方法调取爬取到的源代码,并print出来

有时候这样简单直接的请求会被网站拒绝。这时候我们需要加入一些验证信息,将爬虫伪装成正常的用户。教学用例中并不需要这些伪装,希望了解这些知识请学习推荐文章。
在本范式中,我们将爬取到的源代码直接print输出。除了print操作,我们还可以对源代码做储存,检索等各种处理。掌握了这个简单范式的核心思想,爬取网页源代码的爬虫制作就完成了。

七、教学用例代码详解
我们已经完成了所有前置知识与爬虫技术的学习,现在,让我们直面教学用例提出的任务,并通过代码解决它。
重新阅读一遍任务:
爬取对象:中国玩具和婴童用品协会企业黄页
http://www.wjyt-china.org/pagecontrol.do?action=mygslist&object=ToyCompanyYellowAction
爬取任务:遍历企业黄页上的全部企业,进入每个企业的“公司介绍”页面,在公司介绍文本中搜索“研发”或“研制”字样。统计爬取公司总数,所有公司的名称,含有目标字样公司的数目及名称。
根据任务要求,我们设计出的程序核心行为如下:

打开文件准备储存爬取到的信息
遍历公司黄页中的每个公司:
    储存该公司名称至totalName.txt,将公司总数计数器加一
    进入该公司介绍页面
    若介绍文本中出现“研发”或“研制”字样:
        储存该公司名称至targetName.txt,将目标字样公司数计数器加一
储存公司总数与目标字样公司数至numberInfo.txt
关闭文件

下面给出爬虫的完整代码及其详细注释,如有不理解之处请提取关键字利用搜索引擎搜索:

#coding:utf-8
#author:不饮者
#寂寞不饮者,长愿复清明

import urllib#载入爬虫库
import urllib2
import re#载入正则表达式(RegularExpression)库
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
#在向txt文件写入中文字符时发生了UnicodeError
#这三行代码重置编码环境为utf-8,解决了错误
#该方法搜索自网络

#本函数负责一切公司介绍页面上进行的行为
#访问公司介绍页面,定位介绍文本,搜索目标字样并返还搜索结果
def findTarget(subPageURL):#传入介绍页面URL
    try:
        request = urllib2.Request(subPageURL)#爬取网页源代码的经典范式
        response = urllib2.urlopen(request,timeout=60)
        #设置60秒的超时界限,防止连接某一公司页面时间过长阻塞整个程序进行
        content = response.read().decode('utf-8')
        #将爬取的网页源代码以utf-8格式解析,本网页中含有编码格式为utf-8的中文

        pattern = re.compile('<div class="neiR".*?">(.*?)</div>',re.S)
        #通过正则表达式库re生成公司介绍文本的样式
        items = re.findall(pattern,content)
        #以findall方法在介绍页面源代码中定位介绍文本
        introductionText = u''
        #将储存介绍文本的变量初始化为空的Unicode字符串(一种支持中文的字符串)
        #防止无法搜索到介绍文本时(极特殊情况)程序崩溃
        introductionText = items[0]
        #将正则表达式搜索到的介绍文本储存在变量中

        targetText = u'研发|研制' #目标字样的正则表达式样式
        pattern = re.compile(targetText,re.S)#生成该样式
        isMatch = re.search(pattern,introductionText)
        #通过search方法,在介绍文本中搜索目标字样
        #该函数的返回值是布尔值(只有两个,True或False,相当于“是”和“否”)

        if isMatch:
            return True#如果找到目标字样,本函数返回“是”
        else:
            return False#否则返回“否”

    except urllib2.URLError, e:#异常处理部分
        if hasattr(e,"code"):#打印相关错误信息
            print e.code
        if hasattr(e,"reason"):
            print e.reason
        return False#如果出现异常就视为未找到目标字样
                    #教学用例中没有采取合理的异常处理,而是采用了极为粗糙的做法
                    #这是为了降低代码的复杂程度,让代码便于理解
                    #但会给数据带来误差,实测约为5%

#函数的定义结束了,但函数并不被调用,而是以备之后调用
#下面开始的是程序的核心逻辑
#打开三个储存数据的文件(地址为省略写法)
totalName = open(r'...\totalName.txt','w')#储存全部公司名称
targetName = open(r'...\targetName.txt','w')#储存目标字样公司名称
numberInfo = open(r'...\numberInfo.txt','w')#储存公司总数与目标字样公司数

originURL = r"http://www.wjyt-china.org/pagecontrol.do?action=mygslist&object=ToyCompanyYellowAction"#黄页URL

totalCnt = 0#公司总数计数器
targetCnt = 0#目标字样公司数计数器

try:
    for num in range(1,370):#使用for循环逐一访问1到369页
                            #事实上由于糟糕的异常处理,每爬几十页爬虫就会崩溃
                            #在实际运用的时候每次仅爬取30页
        print num#输出正在处理的页面数以检测程序运行情况(并减轻等待时的无聊感)

        url = originURL+r"&jumpPage="+str(num)
        #改造URL,使得我们可以通过jumpPage访问不同的页面
        request = urllib2.Request(url)#爬取网页源代码范式
        response = urllib2.urlopen(request,timeout=30)#设置30秒的超时界限
        content = response.read().decode('utf-8')

        pattern = re.compile('<div class="neir_neir".*?<a.*?href=".*?shopname=(.*?)">(.*?)</a><span>',re.S)
        #生成公司黄页中公司信息区域的样式
        items = re.findall(pattern,content)#抓取当前页面中全部的公司信息
        for item in items:#遍历抓取的公司信息
            totalCnt += 1#公司总数加一
            companyName = item[1]#抓取出的第二个元素(编程中从0数起)是公司的名称
            companyName.decode('utf-8')#将公司名称转换为utf-8格式
            totalName.write(companyName)#存入totalName.txt
            totalName.write('\n')#再输入一个回车键(否则公司名称会全部连在一起)

            subPage = 'http://www.wjyt-china.org/cnshopcenter/'+item[0]+'/introduction'
            #抓取的第一个元素为公司代号,通过公司代号生成公司介绍页面的URL
            if findTarget(subPage):#调用函数,判断该公司介绍文本中是否存在目标字样
                targetCnt += 1#若存在,则将目标字样公司数加一
                targetName.write(companyName)#并将该公司名称写入targetName.txt
                targetName.write('\n')#再写入一个回车
        totalName.flush()#每处理完一页信息,就将要写入文件的公司名称从缓存刷入硬盘
        targetName.flush()
    #for循环完成后,黄页中全部的公司都已被遍历过
    #此时将公司总数和目标字样公司数写入numberInfo.txt
    numberInfo.write('total number: '+str(totalCnt)+'\n')
    numberInfo.write('target number: '+str(targetCnt))

except urllib2.URLError, e:#异常处理阶段
    if hasattr(e,"code"):#简单地打印异常信息
        print e.code
    if hasattr(e,"reason"):
        print e.reason

#核心逻辑已经全部运行完毕,关闭三个文件
totalName.close()
targetName.close()
numberInfo.close()

print "mission completed"#输出任务完成字样,提示我们程序已运行完毕

本教学用例代码实测可用,能够顺利完成任务目标。但是这段代码也存在着一些问题,最主要的问题就是异常处理能力较差。当发生异常时,较好的情况是产生数据误差,较差的情况则是程序崩溃,导致数据丢失或者数据污染。事实上,各种异常并不算少,例如:有的公司页面信息储存模式和大多数公司不一致,页面丢失的404错误,未知原因的网络服务器500错误。或许是由于靠后的页面维护地较为不好,爬取越进行到后面产生的错误与崩溃越多。当然,总的来看最终误差不足5%,仍在可以接受的范围内。

八、后记
本爬虫是快速学习应用的结果,本教程也不过是一个爬虫技术初学者进行的粗浅总结。可以料想到的是,教程与代码中必然存在着许多疏漏错误。希望随着逐步深入的系统性学习,我能够在爬虫技术,在编程之道上越走越远。或许有一天,再次阅读这篇文章时,我会不禁莞尔一笑。
感谢你的阅读,希望能对你有些许帮助。
再次感谢。

2017.9.10
不饮者
【寂寞不饮者,长愿复清明】
(完)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值