http://blog.sysuschool.com/u/mygod/index.html
请稍候,载入中。。。
 
请稍候,载入中。。。
2020/10/14 20:43:00
博文_关于爬虫之数据提取


本篇小结爬虫之数据提取,并附一个实战例,爬取斗图啦网站的表情包,并准备暂时结束爬虫篇的总结,selenium的用法和scrapt框架,等熟悉一阵子后,有时间再说吧。


前篇小结了利用urllibrequest.urlopen()opener.open()requests.get()requests.post()等可以拿到服务器的响应数据,数据一般为html,即一堆标签及内容,要顺利取出自己需要的数据,需要掌握数据提取之法,本篇小结数据提取。

 

提取html中的数据,我所知的方法有三:lxmlbeautifulsoupre,下面分别说说。 

一、lxml模块、xpath语法

需要安装,pip install lxml

 

# 假如前面获得的响应为text

from lxml import etree  # 导入lxmletree

 

# 指定HTML解析器

parser = etree.HTMLParser(encoding='utf-8')

# 用解析器解析text,返回element对象,含解析数据

html = etree.parse(text, parser=parser)

# 也可以html = etree.HTML(text),让模块自动调用解析器,一般用这个,当然用上面两句更可靠

 

# 接着就可以用xpath获取标签,进而获取标签属性或标签间的内容,如获取id=xxx”的diva标签下的href,即提取其超链接urlxpath括号内的写法即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下位置小于3div

//body/div[@id=xxx]            属性idxxxdiv

//div[contains(@class, "a")]          模糊匹配,属性class"a"

*     所有节点

@*   节点的任何属性

 

这里xpath语法写的不全,不过看几个案例,基本就会了,注意单斜杠和双斜杠的不同,如果是直接下属用单的(儿子),隔了几个标签的用双的(儿子和孙子),xpath返回的是列表,可以遍历或用索引取值。

 

二、BeautifulSoup4

安装 pip install bs4

bs4支持css选择器、python标准库的HTML解析器、lxmlxml解析器

from bs4 import BeautifulSoup

soup = BeautifulSoup(text,"lxml")  # element对象

 

# 利用findfind_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等于eventr标签,注意用class_

alist = soup.find_all('tr', class_='test',id='test')  # 获取id等于testclass等于testa标签

 

# 获取所有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.classtag、及css层级选择器、父子选择等等,如

# 获取所有class等于eventr标签,注意用class_

trs = soup.select('tr'[class_='even'])

trs = soup.select('.even')

trs = soup.select('tr.even')

 

# 取标签属性 xx[属性名]

# 取标签间内容 xx.string单行、strings多行,返回生成器,可遍历、stripped_strings去前后空字符的多行

 

三、re正则表达式

pythonre模块,需导入 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/,上述代码目前亲测没有问题,但如果网站改版的话,那就可能有问题了,现在专业的网站越来越重视反爬技术。

mygod | 阅读全文 | 回复(0) | 引用通告 | 编辑
发表评论:
请稍候,载入中。。。
公告
请稍候,载入中。。。
时间记忆
请稍候,载入中。。。
最新日志
请稍候,载入中。。。
最新评论
请稍候,载入中。。。
最新回复
请稍候,载入中。。。
我的好友
我的相册
站点信息
请稍候,载入中。。。
生活因感动而精彩,理想在创造中放飞
Powered by Oblog.