本篇小结爬虫之数据提取,并附一个实战例,爬取斗图啦网站的表情包,并准备暂时结束爬虫篇的总结,selenium的用法和scrapt框架,等熟悉一阵子后,有时间再说吧。
前篇小结了利用urllib的request.urlopen()、opener.open()或requests.get()、requests.post()等可以拿到服务器的响应数据,数据一般为html,即一堆标签及内容,要顺利取出自己需要的数据,需要掌握数据提取之法,本篇小结数据提取。
提取html中的数据,我所知的方法有三:lxml、beautifulsoup和re,下面分别说说。
一、lxml模块、xpath语法
需要安装,pip install lxml
# 假如前面获得的响应为text
from lxml import etree # 导入lxml的etree
# 指定HTML解析器
parser = etree.HTMLParser(encoding='utf-8')
# 用解析器解析text,返回element对象,含解析数据
html = etree.parse(text, parser=parser)
# 也可以html = etree.HTML(text),让模块自动调用解析器,一般用这个,当然用上面两句更可靠
# 接着就可以用xpath获取标签,进而获取标签属性或标签间的内容,如获取id=“xxx”的div下a标签下的href,即提取其超链接url,xpath括号内的写法即xpath语法
url = html.xpath("//div[@id='xxx']//a/@href")[0] # 注意xpath返回列表
# 如获取该div下第一个a标签间的文本
txt = html.xpath("//div[@id='xxx']//a[1].text()")[0] # 注意xpath中标签索引从1开始
xpath语法:
/ 根节点或某目录下,如 /div指根节点下div,/html、/html/body
// 含子孙节点,如 //div,所有div
@ 属性,如 //div[@id],含id属性的div节点
//body/div[1] 获取body下第一个div,从1开始
//body/div[last()]
获取最后一个div
//body/div[position()<3] 获取body下位置小于3的div
//body/div[@id=‘xxx’] 属性id为xxx的div
//div[contains(@class, "a")] 模糊匹配,属性class含"a"
* 所有节点
@* 节点的任何属性
这里xpath语法写的不全,不过看几个案例,基本就会了,注意单斜杠和双斜杠的不同,如果是直接下属用单的(儿子),隔了几个标签的用双的(儿子和孙子),xpath返回的是列表,可以遍历或用索引取值。
二、BeautifulSoup4库
安装 pip install
bs4
bs4支持css选择器、python标准库的HTML解析器、lxml的xml解析器
from bs4 import BeautifulSoup
soup = BeautifulSoup(text,"lxml") # element对象
# 利用find或find_all获取标签,进而获取标签属性或标签间内容,find返回元素,find_all返回元素列表,如
trs = soup.find_all('tr') # 获取所有tr标签
tr = soup.find_all('tr')[1] # 获取第二个tr标签,索引从0开始
trs = soup.find_all('tr', class_='even') # 获取所有class等于even的tr标签,注意用class_
alist = soup.find_all('tr', class_='test',id='test') # 获取id等于test,class等于test的a标签
# 获取所有a标签的href
alist = soup.find_all('a')
for a in alist:
href =
a['href']
# 获取标签间内容
trs = soup.find_all('tr')[1:] # 用切片去掉第一行,第一个tr是表头
for tr in trs:
tds =
tr.find_all('td')
title =
tds[0].string # .string 标签中文本
# 利用select()方法选择,支持css选择器,如#id、.class、tag、及css层级选择器、父子选择等等,如
# 获取所有class等于even的tr标签,注意用class_
trs = soup.select('tr'[class_='even'])
trs = soup.select('.even')
trs = soup.select('tr.even')
# 取标签属性 xx[属性名]
# 取标签间内容 xx.string单行、strings多行,返回生成器,可遍历、stripped_strings去前后空字符的多行
三、re正则表达式
python的re模块,需导入 import re
result=re.match(正则表达式,要处理的数据字符串)
# 通过group提取,支持分组,即正则的括号分组
re的常用方法还有re.search()搜索、re.findall()查找全部、re.sub()替换、re.split()分割
关于正则表达式,与JavaScript一样,匹配用的字符描述、数量描述、包括贪婪模式等,仅上述方法函数有些不同,特别sub替换,js中更多使用字符串方法replace,这块比较熟悉,就不小结了,本博文主要给自己留资料和作学习梳理用,如有兴趣,可与本人探讨,最后留一个实例作为小结结尾。
实例:爬下图啦的表情包
# 用requests请求获得数据,用lxml解析数据,用xpath和正则提取数据
# 提取出所有页面的表情包的url地址,main函数中for x in range(1,5),这个5表示只爬取5页的表情包,总共有3000多页,如果不怕时间长,可以试试,我爬了1800张,呵呵
# 另外,还有一个多线程版的没有放上来,即还可以使用多线程,充分利用cpu的多核性能,不过python解释器有一个GIL全局锁,并不能发挥多核优势,但处理这种io任务,多线程还是速度要快很多!
代码来了:
import requests
from lxml import etree
from urllib import request
import os
import re
def parse_page(url):
headers =
{
"User-Agent":'Mozilla/5.0 (Windows NT 6.1; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36'
}
respones=requests.get(url, headers=headers)
text=respones.text
html=etree.HTML(text)
imgs=html.xpath("//div[@class='page-content
text-center']//img")
for img in
imgs:
#
print(etree.tostring(img)) # 测试,图片url在属性data-original中
img_url=img.get('data-original')
#
print(img_url)
alt=img.get('alt')
# 正则清除非法字符
alt=re.sub(r'[\??\.,。!!]','',alt)
#
print(alt)
# 从img_url中,分离出后缀名,返回元组
suffix=os.path.splitext(img_url)[1]
#
print(suffix)
filename=alt+suffix
request.urlretrieve(img_url,'images/'+filename)
def main():
# for x in
range(101):
#
url='https://www.doutula.com/photo/list/?page=%d' % x
# parse_page(url)
# break
for x in
range(1,5):
url='https://www.doutula.com/photo/list/?page=%d' % x
parse_page(url)
#
break
if __name__ == '__main__':
main()
里面这个是斗图啦的网址https://www.doutula.com/,上述代码目前亲测没有问题,但如果网站改版的话,那就可能有问题了,现在专业的网站越来越重视反爬技术。 |