寒假留校帮学长写了个爬虫,抓取会议网站上一些CFP信息。想着把一些知识点、坑点记下来,一来做个小总结给工作收收尾,二是以后再遇到好从容应对。
这是我写的第二个比较完善的爬虫了,比第一个要简单许多,完全过程化的代码,而且easychair这个网站页面布局比较友好,适合python新手、前端小白入门练习。但这个网站反爬比较厉害,写爬虫的过程中就被ban了好几次……
目标网站:https://easychair.org/cfp/area.cgi
要抓取的CFP信息按照Area分成24类,随便点开几个链接看看我们要抓取的页面是怎样的。
首先是下方的表格,包含六个字段和一个url链接:
然后多开几个Area,观察它们的url格式:
发现了吗?不同的就是只有最后的数字而已,可以把它们理解成不同的页码。
根据上面的分析,我们已经清楚了要爬取的具体内容,即24个Area页面,每个页面中有一个表格,每条CFP信息中包含7个字段(6个可见字段+一个url链接)。
所以,我们的抓取策略就是:遍历24个Area页面 → 抓取表格信息 → 写入文件或存数据库。是不是很简单?→_→
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:
先简单介绍一下 lxml 库。
它使用的是XPath语法,即XML路径语言,功能是网页解析。同样能实现网页解析功能的还有正则表达式、BeautifulSoup库,各有优劣,一些复杂情形下也会配合使用。一般来说,lxml 的解析速度要优于BeautifulSoup,但语法相对复杂(不过这个不是问题,浏览器早帮我们写好了→_→),至于正则,你得去记规则啊!!!正则能用得很流畅的绝逼大神了。。。
然后利用强大的Chrome浏览器,在要匹配的html代码处右键,点击Copy XPath就得到需要的路径表达式啦!
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。https://github.com/lyandut/CrawlerEasychair.git
其实这个爬虫只是爬了24个大小不同的表格而已,没有进入每个CFP具体的页面,并没有爬出所有字段,所以下篇博文会给大家展示一个全新的爬虫,CrawlerEasychair2.0版。
小剧透一下:2.0版会从本文已经爬取的Link字段入手,涉及到队列、字典写入文件等操作……
综合楼4F
18/02/01晚