bs4是Python中一个非常常用的解析网页获取目标内容的模块,在爬虫中经常被引用。虽然知乎上已有大神比较过不同选择器的速度,实际检测的结论应该是xpath最快,(当然正则表达式更快),然而BeautifulSoup的lxml也不慢。
实际上,大家在爬取过程中都有直观的感觉,大多数时间耗费在发送请求及返回文件(以及刻意休眠等待),实际处理html或json文件的时间相比之下几乎可以忽略不计。鉴于博主最先上手BeautifulSoup并且所需数据量级没有百万那么夸张,于是一直用BeautifulSoup。一个教程几乎搞定所有问题。
实际情况不比教程简单,博主写了几个script后,想分享一些BeautifulSoup具体应用的案例(万一有小伙伴觉得有用呢,笑),全部都是B2B平台公开的企业联系方式数据。
案例1-黄页88
开始URL链接:http://b2b.huangye88.com/shenzhen/fuwu/
目标:爬取所有页中公司名称与联系方式
观察:每页显示公司数量一定;总公司数量在页面中可以找到;翻页后发现URL后加’pn2/’
思路:由开始的URL获取页面,从页面中获取总公司数量,计算可得总页数,通过循环获得所有页面URL,分别访问每个URL获取该页展示的信息
右键查看源代码,我们想要的公司数量在以下代码片段中:
<div class="tittit2"><h3 style="width:40%;">深圳商务服务公司</h3><spanstyle="float:right;padding-right:10px">深圳商务服务名录共收录<em>18908</em>个深圳商务服务公司资料</span></div>
BeautifulSoup有搜索tag的功能,了解html的同学都知道<em>标签是加粗的意思,而我们想要的公司数量正好在这个tag里,如果整个页面只有一个<em>标签,那搜索就轻而易举了。Ctrl+F搜索关键词<em>发现真的只有一个结果。于是公司数量就可以这样提取:
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.109 Safari/537.36'} #伪装成浏览器
page1Url = r” http://b2b.huangye88.com/shenzhen/fuwu/”
page1 = requests.get(page1Url, headers=headers)
soup = BeautifulSoup(page1.text, 'lxml')
companyNo = int(soup.find('em').string)
值得注意的是,为了对抗反爬虫机制,在请求中加入伪装成浏览器的请求头。
一般来说,公司列表会装在一个大div中,并且这个div一般会有class这个属性。右键检查(Chrome)找到盛装公司列表的tag,发现是一个<form>。将页面做成soup之后找到这个<form>,并将其中所有<dl>放进list,每个<dl>包含一家公司的信息,通过循环遍历获取。如下图:
companies = soup.find('form', id='jubao')
if companies == None:
print "Error Page"
return
companies = companies.find_all('dl')
打开一个<dl>标签之后发现,公司名字放在唯一的<h4>标签中,而电话号码在一个itemprop=”tel”的span中。如下图:
h4 = company.find('h4')
name = h4.a.string
phone =company.find('span',attrs={"itemprop":"tel"}).a.string
由于有些公司提供的联系方式是电话,而老板需要将电话与手机分开,所以需要以下代码:
mobile = re.findall("(1\\d{10})",phone) #正则表达式查找并提取所有手机号,一般只能找到1个或者没有
if len(mobile) != 0:
mobile = mobile[0]
telephone = 'NA'
else:
mobile = 'NA'
telephone = phone
最后将公司信息存入字典:
comInfo = {}
comInfo['name'] = name
comInfo['mobile'] = mobile
comInfo['telephone'] = telephone
或者直接写入csv:
with open('hy88.csv','wb') as csvfile: #需要事先import csv
writer =csv.writer(csvfile, dialect='excel')
writer.writerow([name, mobile, telephone])
案例2-网易
开始链接:http://114.163.com/search/2/1983-11-31-2007/?q=&s=10&p=0
目标:爬取北上广深四个城市所有金融/银行/保险业的公司信息
观察:每页显示10间公司,公司总数量在首页显示,翻页后URL最后p=1
思路:类似案例1
右键检查,Ctrl+Shift+C(Chrome快捷键)鼠标移动至希望检查的元素上,对应代码段落就会被高亮,如下图。
发现每条公司信息都被放在class=‘info’的div里面,于是我们通过BeautifulSoup查找到所有并循环遍历。
companies = soup.find_all('div', class_='info')
我们需要的信息有地址,联系电话/手机,电邮。地址是在class=”contact”的<ul>下第一个<li>标签内。但是当该公司没有地址可以显示时,联系电话/手机变成第一个<li>的内容。因此我们通过搜索class=”icon-addr”的<span>定位地址。同理,通过class=”icon-tel”的<span>定位联系方式,通过class=”icon-email”的<span>定位电邮所在。
phone = company.find(class_='icon-tel') #这里的phone实际是一个小图标(icon)
if phone == None: #公司没有联系方式展示
mobile = 'NA'
telephone = 'NA'
else:
phone = phone.previous_element.string #获取联系方式,返回的phone即是上图中“101-85109619”
mobile = re.findall("(1[3458]\\d{9})",phone) #查找是否有手机号
if len(mobile) != 0:
mobile = mobile[0]
telephone = 'NA'
else:
mobile = 'NA'
telephone = phone
email = company.find(class_='icon-email') #这里的email实际是一个小图标
if email != None: #公司有电邮展示,即查找到email图标
email = email.previous_sibling.string
else:
email = 'NA'
address = company.find(class_='icon-addr')
if address == None:
continue
address = address.previous_element.string