Python爬虫理论 | (3) 解析响应

目录

 

1. 解析HTML格式

2.解析JSON格式

3.解析二进制格式

4. 实战


1. 解析HTML格式

解析HTML格式主要有以下几种方法,我们在之后的学习中重点关注前两种:

1)lxml库:第三方库,支持HTML和XML格式解析,支持XPath解析方式,解析效率非常高。

2)BeautifulSoup库:第三方库,支持HTML/XML的解析,支持Python标准库中的HTML解析器,也支持 lxml的 XML解析器。

3)Pyquery库:第三方库,相当于jQuery的python实现,可以用于解析HTML网页等。它的语法与jQuery几乎完全相同。

4)HtmlParser,XML:python自带的,用于解析Html和XML。

  • lxml 

1)XPath基础

XPath, XML 路径语言,用于在XML 文档中查找信息,同样也适用于HTML 文档的搜索。参考文档

XML:可扩展标记语言(eXtensibleMarkupLanguage)

HTML:超文本标记语言(HyperText Markup Language)

 XML和HTML形式上差不太多,都是由一对对层级嵌套的标签构成,如<a>...</a>,标签内部可以有属性、文本等。XML的标签名称可以自己随意取,而HTML的标签大多都是固定的,比如<a>为链接标签,<li>为列表标签等。

 

在XPath中,有七种类型的节点:文档(根)、元素、属性、文本、命名空间、处理指令、注释节点。XML 文档是被作为节点树来对待的。树的根被称为文档节点或者根节点。

 节点关系:

父(Parent) -- book

子(Children) -- title

同胞(Sibling) -- year

先辈(Ancestor) -- bookstore

后代(Descendant) -- price

2) 常用路径表达式

 

XML实例1:

book.xml文件内容:

<?xml version="1.0" encoding="ISO-8859-1"?>
<bookstore>
<book>
  <title lang="eng">Harry Potter</title>
  <price>29.99</price>
</book>
<book>
  <title lang="ch">Learning XML</title>
  <price>39.95</price>
</book>
</bookstore>

from lxml import etree
xml=etree.parse('./book.xml',etree.XMLParser())
result=etree.tostring(xml)
print(xml.xpath("/bookstore/book[1]"))
print(xml.xpath("/bookstore/book[1]/title/text()"))
#[<Element book at 0x1deba754648>](列表)
#[‘Harry Potter’](文本内容)

3)通配符(用来选取未知的XML元素) 

 XML实例2:

book.xml文件内容:

<?xml version="1.0" encoding="ISO-8859-1"?>
<bookstore>
<book>
  <title lang="eng">Harry Potter</title>
  <price>29.99</price>
</book>
<book>
  <title lang="ch">Learning XML</title>
  <price>39.95</price>
</book>
</bookstore>

from lxml import etree
xml=etree.parse('./book.xml',etree.XMLParser())
result=etree.tostring(xml)
print(xml.xpath("/bookstore/*"))
print(xml.xpath("//*"))
#| 是逻辑或 若前一个条件为真时 后一个就不看了 短路效应
print(xml.xpath("/bookstore/title|//book/price"))
print(xml.xpath("//price|title"))
print(xml.xpath("//title|price"))
print(xml.xpath("/bookstore/book/title|//price"))

 

 

HTML实例1:

test.html文件内容:

<div>
    <ul>
         <li id="flask" class="item-0"><a href="link1.html">first item</a></li>
         <li class="item-1"><a href="link2.html">second item</a></li>
         <li class="item-inactive"><a href="link3.html">third item</a></li>
         <li class="item-1"><a href="link4.html">fourth item</a></li>
         <li class="item-0"><a href="link5.html">fifth item</a>
     </ul>
 </div>
from lxml import etree

html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//a')
print(result)    	#1
result = html.xpath('//a/@href')
print(result)  	#2
result = html.xpath('//a[@href]')
print(result)  	#2
result = html.xpath('//a[@href="link4.html"]/text()')
print(result) 	#3
result = html.xpath('//li[contains(@class, "item-1")]')
print(result) 	#4
result = html.xpath('//@class')
print(result) 	#5

  • BeautifulSoup

BeautifulSoup是python的HTML/XML解析库,最主要的功能是从网页抓取数据。

1)它提供了很多函数,用于处理页面的导航、搜索、修改分析树等功能,通过解析文档为用户提供需要抓取的数据。

2)它的代码简单,不需要多少代码就可以写出一个完整的应用程序。

3)它自动将输入文档转换为Unicode编码,输出文档为utf-8编码。

4)它在解析时依赖解析器,支持Python 标准库中的HTML 解析器及第三方解析器(比如 lxml)。由于lxml解析器功能强大,速度快,通常推荐使用lxml解析器 

 

1) 基本对象 

BeautifulSoup将复杂HTML文档解析为树形结构对象进行处理,每个节点为一个对象(所有对象可以归纳为以下4种:Tag , NavigableString, BeautifulSoup, Comment),可以方便地获取指定标签的对应属性,将全部页面转变为字典或者数组,相对于正则表达式的方式,可以简化处理过程。

2)操作方法 

一个Tag可能包含多个字符串或其它Tag,这些都是Tag的子节点。BeatifulSoup提供了很多操作和遍历子节点的属性和方法:

节点选择器:通过遍历文档树,获取标签名或属性信息(如soup.article.li,soup.p.attrs)。

方法选择器:通过搜索文档树,使用find等方法查找相应内容(如soup.find_all(‘li’) )。

CSS选择器—通过搜索文档树,采用CSS格式使用select方法选取特定元素(如soup.select(a[class=‘xxx’]))

节点选择器:

html = '''
<html>
    <head>
        <title>The Dormouse's story</title>
    </head>
<body>
    <p class="title" name="dromouse"><b>The Dormouse's story</b></p>
    <p class="story">Once upon a time there were three little sisters; and their names were
        <a href="http://example.com/elsie" class="sister" id="link1"><!--Elsie--></a>,
        <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
        <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
    </p>
        and they lived at the bottom of a well.
    <p class="story">...</p>
</body>
'''

from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print("-----1.节点选择器(读取字符串soup)-------")
print("soup.p------------")
print(soup.p)  # 选择节点, 第一个匹配的节点 <p class="title" name="dromouse"><b>The Dormouse's story</b></p>
print("soup.p.name------------")
print(soup.p.name)  # 节点名称 p
print("soup.p.attrs------------")
print(soup.p.attrs)  # 节点属性 {'class': ['title'], 'name': 'dromouse'}
print("soup.p.attrs['class']------------")
print(soup.p.attrs['class'])  # 特定属性 ['title']
print("soup.p.string------------")
print(soup.p.string)   # 节点的文本内容 The Dormouse's story
print("soup.p.b------------")
print(soup.p.b)  # 嵌套选择 <b>The Dormouse's story</b>
print("soup.p.contents------------")
print(soup.p.contents)  # 直接子节点,列表类型 [<b>The Dormouse's story</b>]
print("soup.p.parent------------")
print(soup.p.parent)  # 直接父节点<body> <p.../p> </body>
print("list(soup.p.children)------------")
print(list(soup.p.children))  # 直接子节点,生成器类型 [<b>The Dormouse's story</b>]
print("list(soup.p.descendants)------------")
print(list(soup.p.descendants))  # 所有子孙节点 [<b>The Dormouse's story</b>, "The Dormouse's story"]
print("list(soup.p.parents)------------")
print(list(soup.p.parents))  # 所有祖先节点[<body>...</body>,  <html><head>...</body></html>,<html><head>...</body></html>]
print("list(soup.a.next_siblings)------------")
print(list(soup.a.next_siblings))  # 后面的兄弟节点  [',\n        ', <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, ' and\n        ', <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>, ';\n    ']
print("list(soup.a.previous_siblings)------------")
print(list(soup.a.previous_siblings))  # 前面的兄弟节点 ['Once upon a time there were three little sisters; and their names were\n        ']
print("soup.a.next_sibling------------")
print(soup.a.next_sibling)  # 下一个兄弟节点 ,
print("soup.a.previous_sibling------------")
print(soup.a.previous_sibling)  # 上一个兄弟节点 Once upon a time there were three little sisters; and their names were  

方法选择器:

find_all(name, attrs, recursive, text, **kwargs)

1)功能:返回所有匹配元素组成的列表。

2)主要参数:

name:可以传入字符串查找所有名字为 name 的tag,如‘b’ ;可以传入正则表达式作为参数,如re.compile(“^b”);可以传入列表参数,如[“a”, “b”];可以传入True(匹配所有tag,不会返回字符串节点)

recursive:是否需要搜索所有子孙节点,默认为True,表示会检索当前tag的所有子孙节点

keyword:把该参数当作指定名字tag的属性来搜索,如id=‘link2’或href=re.compile("elsie"), id='link1’

limit:当结果到达 limit 值时停止搜索,如limit=3,只返回前3个元素

注意:如果想用class做过滤,但是 class 是 python 的关键词,这时需要在class后加个下划线,如soup.find_all("a", class_=“title")

find(name, attrs, recursive, text, **kwargs)

功能:返回第一个匹配的元素,其余同find_all。

find_parents()和find_parent()

功能:前者返回所有祖先节点, 后者返回直接父节点

find_next_siblings()和find_next_sibling()

 功能:前者返回后面所有的兄弟节点, 后者返回后面第一个兄弟节点 

find_previous_siblings()和find_previous_sibling()

 功能:前者返回前面所有的兄弟节点, 后者返回前面第一个兄弟节点

find_all_next()和find_next()

 功能:前者返回节点后所有符合条件的节点,后者返回第一个符合条件的节点

find_all_previous()和find_previous()

功能:前者返回节点前所有符合条件的节点,后者返回第一个符合条件的节点

 

实例:

test.html内容:

<div>
    <ul>
         <li id="flask" class="item-0"><a href="link1.html">first item</a></li>
         <li class="item-1"><a href="link2.html">second item</a></li>
         <li class="item-inactive"><a href="link3.html">third item</a></li>
         <li class="item-1"><a href="link4.html">fourth item</a></li>
         <li class="item-0"><a href="link5.html">fifth item</a>
     </ul>
 </div>
print("-----2.方法选择器(读取文件soup2)-------")
htmlfile = open('./test.html', 'r', encoding='utf-8')
soup2 = BeautifulSoup(htmlfile.read(), 'lxml')
print("-----find():返回发现的第一个------------")
li = soup2.find('li')
print('find_li:',li) #find_li: <li class="item-0" id="flask"><a href="link1.html">first item</a></li>
print('li.text(返回标签的内容):',li.text) #li.text(返回标签的内容): first item
print('li.attrs(返回标签的属性):',li.attrs) #li.attrs(返回标签的属性): {'id': 'flask', 'class': ['item-0']} 字典形式
print('li.string(返回标签内容为字符串):',li.string) #li.string(返回标签内容为字符串): first item
li = soup2.find(id='flask')
print(li)  #<li class="item-0" id="flask"><a href="link1.html">first item</a></li>
# 因为class是python的保留关键字,所以无法直接查找class这个关键字。有两种方法可以进行class属性查询
# 第一种:在attrs属性用字典进行传递参数
find_class = soup2.find(attrs={'class':'item-1'})
print('findclass:',find_class) #findclass: <li class="item-1"><a href="link2.html">second item</a></li>
# 第二种:使用BeautifulSoup中的特别关键字参数class_
beautifulsoup_class_ = soup2.find(class_ = 'item-1')
print('BeautifulSoup_class_:',beautifulsoup_class_) #BeautifulSoup_class_: <li class="item-1"><a href="link2.html">second item</a></li>
print("-----find_all():返回发现的全部------------")
# find_all 查找所有
li_all = soup2.find_all('li')
for li in li_all:  #1...5(最后一个自动补齐了</li>)
	print('匹配到的li:',li)      #1.<li class="item-0" id="flask"><a href="link1.html">first item</a></li>。。。
	print('li的内容:',li.text)   #1.first item。。。
	print('li的属性:',li.attrs)  #1.{'id': 'flask', 'class': ['item-0']}。。。
# 一种灵活的使用方式
li_quick = soup2.find_all(attrs={'class':'item-1'})
for li in li_quick:
	print('灵活查找:',li)
#灵活查找: <li class="item-1"><a href="link2.html">second item</a></li>
#灵活查找: <li class="item-1"><a href="link4.html">fourth item</a></li>

CSS选择器:

 可以采用CSS的语法格式来筛选元素。

select() :

print("-----3.CSS选择器-------")
print(soup.select('title'))  # title标签 [<title>The Dormouse's story</title>]
print(soup.select("p:nth-of-type(3)"))  # 第三个p节点 [<p class="story">...</p>]
print(soup.select('body a'))  # body下的所有子孙a节点 [<a class="sister" href="http://example.com/elsie" id="link1"><!--Elsie--></a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
print(soup.select('p > a'))  # 所有p节点下的所有a直接节点 [<a class="sister" href="http://example.com/elsie" id="link1"><!--Elsie--></a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
print(soup.select('p > #link1'))  # 所有p节点下的id=link1的直接子节点 [<a class="sister" href="http://example.com/elsie" id="link1"><!--Elsie--></a>]
print(soup.select('#link1 ~ .sister'))  # id为link1的节点后面class=sister的所有兄弟节点 [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
print(soup.select('#link1 + .sister'))  # id为link1的节点后面class=sister的第一个兄弟节点 [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
print(soup.select('.sister'))  # class=sister的所有节点 [<a class="sister" href="http://example.com/elsie" id="link1"><!--Elsie--></a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
print(soup.select('[class="sister"]'))  # class=sister的所有节点 [<a class="sister" href="http://example.com/elsie" id="link1"><!--Elsie--></a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
print(soup.select("#link1"))  # id=link1的节点 [<a class="sister" href="http://example.com/elsie" id="link1"><!--Elsie--></a>]
print(soup.select("a#link1"))  # a节点,且id=link1的节点 [<a class="sister" href="http://example.com/elsie" id="link1"><!--Elsie--></a>]
print(soup.select('a[href]'))  # 所有的a节点,有href属性 [<a class="sister" href="http://example.com/elsie" id="link1"><!--Elsie--></a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
print(soup.select('a[href="http://example.com/elsie"]'))  # 指定href属性值的所有a节点 [<a class="sister" href="http://example.com/elsie" id="link1"><!--Elsie--></a>]
print(soup.select('a[href^="http://example.com/"]')) # href属性以指定值开头的所有a节点 [<a class="sister" href="http://example.com/elsie" id="link1"><!--Elsie--></a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
print(soup.select('a[href$="tillie"]'))  # href属性以指定值结尾的所有a节点 [<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
print(soup.select('a[href*=".com/el"]'))  # 支持正则匹配 [<a class="sister" href="http://example.com/elsie" id="link1"><!--Elsie--></a>]

 

2.解析JSON格式

json.dumps()和json.loads()是json格式处理函数。

json.loads():将已编码的json字符串解码为字典类型。

json.dumps():将Python数据类型列表(字典)转化为json字符串。

import json 
#json.dumps将字典转化为字符串 
dict1 = {"age": "18"} 
json_info = json.dumps(dict1) 
print("dict1的类型:"+str(type(dict1))) 	#dict
print("通过json.dumps()函数处理:") 
print(“json_info "+ json_info)     	#{"age": "18"} 
print("json_info的类型:"+str(type(json_info))) 	#str
dict2 = json.loads(json_info)
print("dict2的类型:"+str(type(dict2))) 			#dict
print(dict2) 						#{'age': '18'}

#URL请求-响应结果是json
url='http://httpbin.org/get”
r = requests.get(url)
result  = json.loads(r.text)

json.dump()和json.load()主要用来读写json文件函数。

#写文件
json_info = "{'age': '12'}"
file = open('1.json','w',encoding='utf-8')
json.dump(json_info,file)

#读文件
file = open('1.json','r',encoding='utf-8')
print(json.load(file))    	#{'age': '12'}

 

3.解析二进制格式

图片、音频、视频这些文件实际上都是由二进制码组成的。

import os
os.makedirs('./image/', exist_ok=True)
url = "https://www.python.org/static/img/python-logo.png"


#方法1 直接将远程数据下载到本地文件
from urllib.request import urlretrieve
urlretrieve(url, './image/img1.png')


#方法2 将响应结果以二进制形式写入到指定文件中
import requests 
r = requests.get(url)
with open('./image/img2.png', 'wb') as f:  
    f.write(r.content)

#方法3 相对内置的open()来说,codecs.open方法比较不容易在编码上出现问题,返回unicode编码
import urllib
import requests
import os
import codecs

bytes = urllib.request.urlopen(url)
f=codecs.open('./image/img3.png','wb')
f.write(bytes.read())
f.flush() 
f.close()

4. 实战

  • 爬取万方数据库文献摘要

输入指定关键词,分别爬取与该关键词相关的期刊、学位、会议论文的摘要、题目、作者等信息。

网址:http://www.wanfangdata.com.cn

完整项目

  • 爬取百度图片

指定搜索词,爬取与搜索词相关的图片。

网址:http://image.baidu.com

完整项目

  • 爬取搜狗图片

指定搜索词,爬取与搜索词相关的图片。

网址:https://pic.sogou.com/

完整项目

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值