人生苦短,我用Python(二)— 爬取会议网站 EasyChair Smart CFP

寒假留校帮学长写了个爬虫,抓取会议网站上一些CFP信息。想着把一些知识点、坑点记下来,一来做个小总结给工作收收尾,二是以后再遇到好从容应对。

这是我写的第二个比较完善的爬虫了,比第一个要简单许多,完全过程化的代码,而且easychair这个网站页面布局比较友好,适合python新手、前端小白入门练习。但这个网站反爬比较厉害,写爬虫的过程中就被ban了好几次……

制定抓取策略

目标网站:https://easychair.org/cfp/area.cgi

要抓取的CFP信息按照Area分成24类,随便点开几个链接看看我们要抓取的页面是怎样的。

首先是下方的表格,包含六个字段和一个url链接:
人生苦短,我用Python(二)— 爬取会议网站 EasyChair Smart CFP_第1张图片
然后多开几个Area,观察它们的url格式:
url1.png
url2.png
发现了吗?不同的就是只有最后的数字而已,可以把它们理解成不同的页码。

根据上面的分析,我们已经清楚了要爬取的具体内容,即24个Area页面,每个页面中有一个表格,每条CFP信息中包含7个字段(6个可见字段+一个url链接)。

所以,我们的抓取策略就是:遍历24个Area页面 → 抓取表格信息 → 写入文件或存数据库。是不是很简单?→_→
人生苦短,我用Python(二)— 爬取会议网站 EasyChair Smart CFP_第2张图片
Ps:(很重要,敲黑板)

写一个爬虫,一定是从思路开始的,也就是从制定抓取策略开始的。一定要搞清楚自己的爬虫要做什么,复杂度大概是怎样的,需要有哪几部分的功能,是否需要模块化编程……虽然有很多优秀的爬虫框架,但别人写的东西并不是随便拿来就能用的,对于不同的网站,布局不同,前端所用的技术不同,下载解析过程中可能遇到的问题更是不尽相同,而且你的问题坑点别人也未必遇到过。记得实验室大佬曾经说过一句话:“我很想帮你,但事实上我什么也帮不了你。”

编写框架代码

根据我们制定的抓取策略,我们写出以下代码:

import urllib2

for num in range(1,25):
    url = 'https://easychair.org/cfp/area.cgi?area=' + str(num)
    headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'}

    try:
        # 下载网页
        request = urllib2.Request(url, headers=headers)
        response = urllib2.urlopen(request)
        html = response.read()
        response.close()

        '''
            解析网页,抓取字段
        '''

        '''
            写入文件或数据库
        '''    
    except urllib2.URLError, e:
        if hasattr(e, 'code'):
            print e.code
        if hasattr(e, 'reason'):
            print e.reason

Ps:

  • 如果按照上面的代码写文件,最终应该生成24个文件,也就是说每次循环写入的文件名应该随num的改变而改变,不然最后只有num=24的CFP信息。
  • 如果想把所有信息写入一个文件,把write部分代码放在循环外面就行啦。

利用 lxml 库抓取字段

先简单介绍一下 lxml 库。

它使用的是XPath语法,即XML路径语言,功能是网页解析。同样能实现网页解析功能的还有正则表达式、BeautifulSoup库,各有优劣,一些复杂情形下也会配合使用。一般来说,lxml 的解析速度要优于BeautifulSoup,但语法相对复杂(不过这个不是问题,浏览器早帮我们写好了→_→),至于正则,你得去记规则啊!!!正则能用得很流畅的绝逼大神了。。。

然后利用强大的Chrome浏览器,在要匹配的html代码处右键,点击Copy XPath就得到需要的路径表达式啦!
人生苦短,我用Python(二)— 爬取会议网站 EasyChair Smart CFP_第3张图片
Ps:
如果发现提取出的XPath匹配为空,原因是Chrome会自动添加标签,去掉即可。

关于 lxml 库的安装和XPath语法,网上有许多讲解很详细的教程、博客,这里推荐一下极客学院的python课程:http://www.jikexueyuan.com/course/902.html,老师用的例子非常简单生动,十分钟就可以基本掌握。

个人觉得最最基础的就是下面四个啦:

  • // 定位根节点
  • / 下层寻找
  • /text() 提取文本内容
  • /@xxx 提取属性内容

还有两种比较常用的特殊用法:

  • //*[starts-with(@id,”row”)] 提取id属性以row开头的标签内容
  • xpath(‘string(.)’)处理标签套标签的情况,不过我一般喜欢写成xpath(‘string(.)’).replace(‘\n’,”).strip()),去掉换行符和字段前后的空格

掌握上述几种用法,看懂下面的代码就没什么问题了:

from lxml import etree

'''
    lxml 抓取字段
'''
selector = etree.HTML(html)

# 提取area    //*[@id="content"]/div[2]/div[@class="pagetitle"]
area = selector.xpath('//*[@id="content"]/div[2]/div[@class="pagetitle"]/text()')[0].replace('CFPs in ','')

# 行:  //*[@class="yellow"]
rows = selector.xpath('//*[starts-with(@id,"row") and @class="yellow"]')
# 列
tds = []
for row in rows:
    tds.append(row.xpath('td'))  #tds是一个大小等于行数的列表,其中每个元素td是大小为6的列表
link = [] 
acro = []
name = []
loca = []
dead = []
star = []
topi = []
for td in tds:
    # 下面都是长度等于行数的列表,分别是抓取的7个字段
    link.append(td[0].xpath('a/@href')[0])
    acro.append(td[0].xpath('string(.)').replace('\n','').strip())
    #name.append(td[1].xpath('string(.)').replace('\n','').strip())
    name.append(td[1].xpath('text()')[0])
    loca.append(td[2].xpath('string(.)').replace('\n','').strip())
    dead.append(td[3].xpath('string(.)').replace('\n','').strip())
    star.append(td[4].xpath('string(.)').replace('\n','').strip())
    topi.append(td[5].xpath('a/span/text()'))

# 利用zip()函数组合上面各列表,最后加一个area字段
zipped = zip(acro, name, link, loca, dead, star, topi, [area]*len(acro))

写文件操作

将zipped写入csv文件:

import csv
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
# 写入文件
with open(str(num)+" "+area+".csv","wb") as f: 
    writer = csv.writer(f)
    #先写入表头  Acronym (link), name, location, deadline, start_date, topic
    writer.writerow(["Acronym","Name","Link","Location","Submission deadline","Start date","Topics","Area"])
    #写入多行用writerows
    writer.writerows(zipped)

Ps:

  • sys.setdefaultencoding(‘utf-8’)解决编码错误UnicodeEncodeError,但这种一劳永逸的做法并不妥当,详情可以看下这篇博文:https://blog.ernest.me/post/python-setdefaultencoding-unicode-bytes。
  • 以“wb”方式打开文件,防止出现空行。
  • 这种把列表写入文件的操作比较简单,但仅适用于抓取的字段数目是确定而且比较一致的情况。有时候我们的任务需求可能是“抓取所有字段”(博主接到的就是这样的要求(⊙﹏⊙)),比如某条CFP有截止日期字段,但另一条CFP可能还未发布截止日期,这时候我们就要利用字典代替列表去存数据,然后把字典写入文件。后续的博文会介绍到这种操作……

爬取结果展示

  • 运行结果:
    人生苦短,我用Python(二)— 爬取会议网站 EasyChair Smart CFP_第4张图片
  • 24个csv文件:
    人生苦短,我用Python(二)— 爬取会议网站 EasyChair Smart CFP_第5张图片
  • 文件内容:
    人生苦短,我用Python(二)— 爬取会议网站 EasyChair Smart CFP_第6张图片

GitHub源码

https://github.com/lyandut/CrawlerEasychair.git

最后

其实这个爬虫只是爬了24个大小不同的表格而已,没有进入每个CFP具体的页面,并没有爬出所有字段,所以下篇博文会给大家展示一个全新的爬虫,CrawlerEasychair2.0版。

小剧透一下:2.0版会从本文已经爬取的Link字段入手,涉及到队列、字典写入文件等操作……
人生苦短,我用Python(二)— 爬取会议网站 EasyChair Smart CFP_第7张图片
综合楼4F

18/02/01晚

你可能感兴趣的:(Python)