以天涯论坛的简单网页爬取为例子,BeautifulSoup、PyQuery以及Xpath都使用一下。
1. 网页访问
使用requests库访问
import requests
url='http://bbs.tianya.cn/hotArticle.jsp?pn=1' #天涯论坛
req=requests.get(url)
req
输出:<Response [200]>
表示能够成功访问
展示下网页源代码:
print(req.text)
2. 确定想要抓取的内容
打开开发者工具,网页源代码。现在想要提取这个页面的帖子标题、帖子作者以及发布时间。
每一个帖子的三条信息都包含在<tr class> 的节点里面
分别在并列的3个<td> 标签内
<td>节点下还包括<a>节点
3. 定位位置,抓取信息
3.1 使用BeautifulSoup
首先解析网页:
from bs4 import BeautifulSoup
html=req.text
#html1=req.content
soup=BeautifulSoup(html,'lxml')
#soup=BeautifulSoup(html1,'html.parser')
soup
现在知道标题都在一个<td class=“td-title”>的节点里,可以使用find_all函数,找到所有节点,遍历选择需要的内容。
注意点: 因为是要写循环,遍历三个td节点,找td的上一个节点作为操作对象(tr节点)
选择:
#内容都在tr节点内,可以先找出所有的tr节点
contents_bs=soup.find_all('tr')
for item in contents_bs:
#帖子标题
try:
title=item.find('td',attrs={'class':'td-title'})
title=title.text
except:
title=None
#作者
try:
author=item.find('a',attrs={'class':'author'})
author=author.text
except:
author=None
#时间
try:
time=item.select('tr td:nth-child(3)') #css选择器结果是列表
time=[i.get_text() for i in time]
#结果是列表,列表内一个元素,将这个元素提出来,
#且css选择器的文本提取方式是get_text()函数
except:
time=None
print(title,author,time)
print('\n')
问题:
1 为什么要用try-except?
因为是对tr节点,选下面的td节点,但第一个是空的,None,选择文本内容时会报错,所以添加try-except,跳过出错的
2 find函数与css选择器的使用?
Beautifulsoup可以使用标准选择器(find,find_all),也可以使用css选择器,find_all函数是选择出全部符合的内容,find是选择出第一个符合条件的内容,因为这里帖子标题和作者所在节点都有其各自的属性,使用find就可以定位,第一个就能够选出来。
但时间所在节点,就一个标签名td,没有能够标识的属性,就使用了css选择器,定位第三个td节点。
3 time=[i.get_text() for i in time] 的使用?
css选择器结果是列表,选出的td标签在列表中,需要先将其从列表中提出来,成为正常的网页语句,<td>…</td>,才能用get_text()提取文本内容。
3.2 使用PyQuery
首先解析网页:
from pyquery import PyQuery
doc=PyQuery(req.text)
print(doc)
同样的,定位节点,循环遍历
注意点: PyQuery使用**.items**将节点内容选择出来
选择:
contents_pq=doc('div tr') #先选节点,再用items()
for item in contents_pq.items():
title=item('td:nth-child(1)').text() #使用的是css选择器
author=item('td:nth-child(2)').text()
time=item('td:nth-child(3)').text()
print(title,author,time)
print('\n')
或者另一种写法
for item in doc.items('div tr'): #doc后面跟.items()
title=item('td:nth-child(1)').text() #使用的是css选择器
author=item('td:nth-child(2)').text()
time=item('td:nth-child(3)').text()
print(title,author,time)
print('\n')
(这里没有报错,没有用try-except)
3.3 使用Xpath
首先解析网页:
#使用xpath
from lxml import etree
html=etree.HTML(req.text)
print(html)
输出:<Element html at 0x6bd2f40>
注意点:
1 xpath插件很好用,结合起来会十分方便。
2 xpath的路径是链式的
选择:
title_s=html.xpath("//tbody//tr")
for item in title_s:
#标题
title=item.xpath("td[@class='td-title']/a/text()")
title=''.join(title)
#作者
author=item.xpath("td//a[@class='author']/text()")
author=''.join(author)
#时间
time=item.xpath("td[3]/text()")
time=''.join(time)
print(title,author,time)
这里xpath的使用,同样是先选择大的节点,在大节点中,遍历需要的小节点选取内容,与上面两种方式的逻辑相同,只是xpath的路径,有xpath插件的加持,个人觉得更好写!
问题:
为什么会有 ‘’.join() 语句,是因为xpath选择出来的文本内容,是在一个列表里,这个列表包含一个元素,要保存文本内容,就需要将字符串的文本内容提取出来,使用上面的函数可以实现这个效果。
以上便是三种解析器在解析天涯论坛时,不同的选取方式,针对不同的网页,还有可能碰到各种细节问题。
总的来说,个人还是倾向使用xpath,比较方便。
4. 保存抓取内容
这里使用上面的xpath语句,将内容保存到csv文件中
import csv
file='test1.csv'
csvf=open(file,'a+',encoding='utf-8',newline='')
writer=csv.writer(csvf)
writer.writerow(('标题','作者','发布时间'))
title_s=html.xpath("//tbody//tr")
for item in title_s:
#标题
title=item.xpath("td[@class='td-title']/a/text()")
title=''.join(title)
#作者
author=item.xpath("td//a[@class='author']/text()")
author=''.join(author)
#时间
time=item.xpath("td[3]/text()")
time=''.join(time)
writer.writerow((title,author,time))
csvf.close()
但文字乱码,encoding='gbk’时,会报错
" ‘gbk’ codec can’t encode character ‘\u200b’ in position 7: illegal multibyte sequence"
改成encoding=‘utf-8’,但保存下来的文件乱码(解决方式暂时是先用记事本打开,以utf-8编码另存csv文件,可以转换回来)
如何设置encoding,或者说如何解决导出数据乱码,还没有找到好的解决方式,以后解决了再更。