【python】爬虫基础——JSON、requests、BeautifulSoup、lxml、爬取静态网页

概念

爬虫(spider,⼜叫网络爬虫),是指向⽹站/网络发起请求,获取资源后分析并提取有用数据的程序。

通过程序模拟浏览器请求站点的行为,把站点返回的HTML代码/JSON数据/⼆进制数据(图片、

视频) 爬到本地,进而提取自己需要的数据,存放起来使用。

步骤

  1. 发送请求
    1. 请求方式:GET、POST
    2. 请求URL
    3. 请求头:User-Agent、Host、Cookies等
  2. 获取数据
    1. 响应状态
    2. 响应头
    3. 响应体:要获取的数据
  3. 解析数据
    1. 正则表达式
    2. lxml
    3. BeautifulSoup
  4. 存储数据
    1. 文本
    2. 数据库
    3. 二进制文件

安装常用包

requests包、bs4包和lxml包

cmd执行

conda info -e	#查看所有环境

pip list	#查看当前环境下面有哪些包

conda install requests	#安装requests包

conda install lxml	#安装lxml包

conda install bs4	#安装bs4包

在这里插入图片描述

数据提取


1. 响应内容的分类

在发送请求获取响应之后,可能存在多种不同类型的响应内容;而且很多时候,我们只需要响应内容中的一部分数据

  • 结构化的响应内容

    • json字符串

      • 可以使用re、json等模块来提取特定数据
    • xml字符串

      • 可以使用re、lxml等模块来提取特定数据

      • xml字符串的例子如下

        <bookstore>
        <book category="COOKING">
          <title lang="en">Everyday Italian</title> 
          <author>Giada De Laurentiis</author> 
          <year>2005</year> 
          <price>30.00</price> 
        </book>
        <book category="CHILDREN">
          <title lang="en">Harry Potter</title> 
          <author>J K. Rowling</author> 
          <year>2005</year> 
          <price>29.99</price> 
        </book>
        <book category="WEB">
          <title lang="en">Learning XML</title> 
          <author>Erik T. Ray</author> 
          <year>2003</year> 
          <price>39.95</price> 
        </book>
        </bookstore>
        
  • 非结构化的响应内容

    • html字符串

      • 可以使用re、lxml等模块来提取特定数据
      • html字符串的例子如下图

2. 认识xml以及和html的区别

要搞清楚html和xml的区别,首先需要我们来认识xml

2.1 认识xml

xml是一种可扩展标记语言,样子和html很像,功能更专注于对传输和存储数据

<bookstore>
<book category="COOKING">
  <title lang="en">Everyday Italian</title> 
  <author>Giada De Laurentiis</author> 
  <year>2005</year> 
  <price>30.00</price> 
</book>
<book category="CHILDREN">
  <title lang="en">Harry Potter</title> 
  <author>J K. Rowling</author> 
  <year>2005</year> 
  <price>29.99</price> 
</book>
<book category="WEB">
  <title lang="en">Learning XML</title> 
  <author>Erik T. Ray</author> 
  <year>2003</year> 
  <price>39.95</price> 
</book>
</bookstore>

上面的xml内容可以表示为下面的树结构:

在这里插入图片描述

2.2 xml和html的区别

在这里插入图片描述

  • html:
    • 超文本标记语言
    • 为了更好的显示数据,侧重点是为了显示
  • xml:
    • 可扩展标记语言
    • 为了传输和存储数据,侧重点是在于数据内容本身

2.3 常用数据解析方法

在这里插入图片描述

JSON模块

JSON简介

JSON是⼀种存储和交换数据的语法

JSON仅仅是⽂本,它能够轻松地在服务器浏览器之间传输

JSON的数据格式其实就是python里面的字典格式

JSON语法

格式1:JSON 对象

{"name": "小周", "sex":}

格式2:JSON 数组

{
    "student":
        [
            {"name": "小潇", "sex": "男"},
            {"name": "小周", "sex": "女"},
            {"name": "小小", "sex": "男"}
        ]
}

方法

import json

json.dumps()

将python数据类型转换为json格式的字符串

d = {'a':1, 'b':2}
print(d)
print(type(d))

json_d = json.dumps(d)
print(json_d)
print(type(json_d))

# 打开文件   在文件里写入转成的json串
with open('data.json', 'w', encoding='utf-8') as f:  
	f.write(json_d)
{'a': 1,'b': 2}
<class 'dict'>
{"a": 1,"b": 2}
<class 'str'>

json.dump()

将python数据类型转换并保存到json格式文件内

  • sort_keys: 是否排序 indent: 定义缩进距离
  • separators: 是一个元组,定义分隔符的类型
  • skipkeys:是否允许json字串编码字典对象时 字典的key不是字符串类型
d = {'a':1,'b':2}
json.dump(d,open('data.json','w'),sort_keys=True,indent=4,separators=(',',': '))

json.loads()

将json格式的字符串转换为python的类型

d = {'a':1, 'b':2}
print(d)
print(type(d))

json_d = json.dumps(d)
print(json_d)
print(type(json_d))

dic_d = json.loads(json_d)		#json.loads()
print(dic_d)
print(type(dic_d))

#文件操作
f = open('data.json', encoding='utf-8')
content = f.read()  # 使用loads()方法需要先读文件
python_obj = json.loads(content)
print(python_obj)
print(type(python_obj))	 #<class 'dict'>
{'a': 1,"b": 2}
<class 'str'>
{'a': 1,'b': 2}
<class 'dict'>

json.load()

从json格式的文件中读取数据并转换为python的类型

python_obj = json.load(open('data.json','r'))
print(python_obj)
print(type(python_obj))		#<class 'dict'>

jsonpath模块


jsonpath模块的使用场景

如果有一个多层嵌套的复杂字典,想要根据key和下标来批量提取value,这是比较困难的,jsonpath模块就能解决这个痛点。

jsonpath可以按照key对python字典进行批量数据提取

jsonpath模块的安装

pip install jsonpath -i https://pypi.tuna.tsinghua.edu.cn/simple

jsonpath模块使用

from jsonpath import jsonpath

jsonpath语法规则

在这里插入图片描述

jsonpath使用示例

book_dict = { 
  "store": {
    "book": [ 
      { "category": "reference","author": "Nigel Rees","title": "Sayings of the Century","price": 8.95
      },{ "category": "fiction","author": "Evelyn Waugh","title": "Sword of Honour","price": 12.99
      },"author": "Herman Melville","title": "Moby Dick","isbn": "0-553-21311-3","price": 8.99
      },"author": "J. R. R. Tolkien","title": "The Lord of the Rings","isbn": "0-395-19395-8","price": 22.99
      }
    ],"bicycle": {
      "color": "red","price": 19.95
    }
  }
}

from jsonpath import jsonpath

print(jsonpath(book_dict,'$..author')) # 如果取不到将返回False # 返回列表,如果取不到将返回False

在这里插入图片描述

jsonpath练习

我们以拉勾网城市JSON文件 http://www.lagou.com/lbs/getAllCitySearchLabels.json 为例,获取所有城市的名字的列表,并写入文件。

参考代码:

import requests
import jsonpath
import json

# 获取拉勾网城市json字符串
url = 'http://www.lagou.com/lbs/getAllCitySearchLabels.json'
headers = {"User-Agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)"}
response =requests.get(url, headers=headers)
html_str = response.content.decode()

# 把json格式字符串转换成python对象
jsonobj = json.loads(html_str)

# 从根节点开始,获取所有key为name的值
citylist = jsonpath.jsonpath(jsonobj,'$..name')

# 写入文件
with open('c','w') as f:
    content = json.dumps(citylist, ensure_ascii=False)
    f.write(content)

输出结果:city_name.txt

["安阳","安庆","安康","鞍山","安顺","澳门","阿拉善盟","阿坝藏族羌族自治州","阿拉尔","北京","保定","包头","滨州","蚌埠","宝鸡","北海","亳州","毕节","百色","保山","巴音郭楞","巴中","本溪","白银","巴彦淖尔","白城","白山","北屯","成都","长沙","重庆","长春","常州","沧州","郴州","赤峰","承德","常德","潮州","滁州","朝阳","楚雄","澄迈","池州","昌吉","崇左","昌都","东莞","大连","德州","德阳","大理","东营","大同","达州","大庆","丹东","德宏","儋州","定西","大兴安岭","鄂尔多斯","鄂州","恩施","佛山","福州","阜阳","抚州","阜新","抚顺","防城港","广州","贵阳","赣州","桂林","广安","贵港","甘孜藏族自治州","广元","甘南","杭州","合肥","惠州","哈尔滨","海口","呼和浩特","湖州","邯郸","菏泽","衡水","淮安","海外","衡阳","怀化","黄石","黄冈","淮南","淮北","河源","鹤壁","汉中","黑河","河池","呼伦贝尔","红河","黄山","葫芦岛","哈密","海东","贺州","黄南","济南","金华","嘉兴","江门","济宁","揭阳","九江","晋中","荆州","焦作","锦州","景德镇","吉林","佳木斯","吉安","晋城","荆门","金昌","济源","酒泉","嘉峪关","昆明","开封","喀什","克拉玛依","廊坊","兰州","洛阳","临沂","聊城","柳州","连云港","乐山","六安","丽水","临汾","泸州","漯河","龙岩","吕梁","丽江","凉山彝族自治州","拉萨","六盘水","娄底","辽阳","临沧","陵水黎族自治县","辽源","临夏","林芝","来宾","陇南","绵阳","眉山","茂名","梅州","马鞍山","牡丹江","南京","宁波","南昌","南宁","南通","南阳","宁德","南充","内江","南平","莆田","盘锦","濮阳","平顶山","萍乡","普洱","攀枝花","平凉","青岛","泉州","清远","秦皇岛","曲靖","衢州","齐齐哈尔","钦州","琼海","庆阳","七台河","黔南","黔西南","黔东南","日照","日喀则","上海","深圳","苏州","沈阳","石家庄","汕头","绍兴","宿迁","商丘","三亚","上饶","韶关","宿州","十堰","汕尾","遂宁","邵阳","绥化","随州","三门峡","三明","四平","松原","朔州","石嘴山","石河子","商洛","山南","神农架林区","天津","太原","台州","唐山","泰州","泰安","通辽","铜仁","通化","铜陵","铁岭","天水","台湾","铜川","天门","铁门关","武汉","无锡","温州","潍坊","芜湖","乌鲁木齐","威海","渭南","梧州","乌兰察布","武威","文山","万宁","文昌","乌海","五家渠","五指山","西安","厦门","徐州","新乡","襄阳","邢台","咸阳","香港","许昌","西宁","孝感","信阳","新余","湘潭","咸宁","宣城","西双版纳","仙桃","忻州","湘西土家族苗族自治州","兴安盟","锡林郭勒盟","烟台","扬州","银川","盐城","宜宾","宜昌","阳江","玉林","岳阳","宜春","运城","益阳","营口","榆林","玉溪","雅安","云浮","永州","阳泉","鹰潭","延边","伊犁","伊春","延安","郑州","珠海","中山","淄博","株洲","漳州","湛江","肇庆","镇江","遵义","周口","枣庄","驻马店","张家口","长治","舟山","张掖","资阳","昭通","自贡","张家界","中卫"]

http协议复习

http以及https的概念和区别

HTTPS比HTTP更安全,但是性能更低

  • HTTP:超文本传输协议,默认端口号是80
    • 超文本:是指超过文本,不仅限于文本;还包括图片、音频、视频等文件
    • 传输协议:是指使用共用约定的固定格式来传递转换成字符串的超文本内容
  • HTTPS:HTTP + SSL(安全套接字层),即带有安全套接字层的超本文传输协,默认端口号:443
    • SSL对传输的内容(超文本,也就是请求体或响应体)进行加密
  • 可以打开浏览器访问一个url,右键检查,点击net work,点选一个url,查看http协议的形式

爬虫特别关注的请求头和响应头

特别关注的请求头字段

在这里插入图片描述

http请求的形式如上图所示,爬虫特别关注以下几个请求头字段

  • Content-Type
  • Host (主机和端口号)
  • Connection (链接类型)
  • Upgrade-Insecure-Requests (升级为HTTPS请求)
  • User-Agent (浏览器名称)
  • Referer (页面跳转处)
  • Cookie (Cookie)
  • Authorization(用于表示HTTP协议中需要认证资源的认证信息,如前边web课程中用于jwt认证)

加粗的请求头为常用请求头,在服务器被用来进行爬虫识别的频率最高,相较于其余的请求头更为重要,但是这里需要注意的是并不意味这其余的不重要,因为有的网站的运维或者开发人员可能剑走偏锋,会使用一些比较不常见的请求头来进行爬虫的甄别

特别关注的响应头字段

在这里插入图片描述

http响应的形式如上图所示,爬虫只关注一个响应头字段

  • Set-Cookie (对方服务器设置cookie到用户浏览器的缓存)

常见的响应状态码

  • 200:成功
  • 302:跳转,新的url在响应的Location头中给出
  • 303:浏览器对于POST的响应进行重定向至新的url
  • 307:浏览器对于GET的响应重定向至新的url
  • 403:资源不可用;服务器理解客户的请求,但拒绝处理它(没有权限)
  • 404:找不到该页面
  • 500:服务器内部错误
  • 503:服务器由于维护或者负载过重未能应答,在响应中可能可能会携带Retry-After响应头;有可能是因为爬虫频繁访问url,使服务器忽视爬虫的请求,最终返回503响应状态码

服务器给我的相关反馈,我们在学习的时候就被教育说应该将真实情况反馈给客户端,但是在爬虫中,可能该站点的开发人员或者运维人员为了阻止数据被爬虫轻易获取,可能在状态码上做手脚,也就是说返回的状态码并不一定就是真实情况,比如:服务器已经识别出你是爬虫,但是为了让你疏忽大意,所以照样返回状态码200,但是响应体重并没有数据。

所有的状态码都不可信,一切以是否从抓包得到的响应中获取到数据为准

浏览器的运行过程

在这里插入图片描述

http请求的过程
  1. 浏览器在拿到域名对应的ip后,先向地址栏中的url发起请求,并获取响应
  2. 在返回的响应内容(html)中,会带有css、js、图片等url地址,以及ajax代码,浏览器按照响应内容中的顺序依次发送其他的请求,并获取相应的响应
  3. 浏览器每获取一个响应就对展示出的结果进行添加(加载),js,css等内容会修改页面的内容,js也可以重新发送请求,获取响应
  4. 从获取第一个响应并在浏览器中展示,直到最终获取全部响应,并在展示的结果中添加内容或修改————这个过程叫做浏览器的渲染
注意:

但是在爬虫中,爬虫只会请求url地址,对应的拿到url地址对应的响应(该响应的内容可以是html,css,js,图片等)

浏览器渲染出来的页面和爬虫请求的页面很多时候并不一样,是因为爬虫不具备渲染的能力(当然后续课程中我们会借助其它工具或包来帮助爬虫对响应内容进行渲染)

  • 浏览器最终展示的结果是由多个url地址分别发送的多次请求对应的多次响应共同渲染的结果
  • 所以在爬虫中,需要以发送请求的一个url地址对应的响应为准来进行数据的提取

requests包

import requests

常用方法

方法 描述
requests.request(url) 构造一个请求,支持以下各种方法
requests.get() 发送一个Get请求
requests.post() 发送一个Post请求
requests.head() 获取HTML的头部信息
requests.put() 发送Put请求
requests.patch() 提交局部修改的请求
requests.delete() 提交删除请求

常用属性或方法

属性或方法 描述
.status_code 响应状态码
.content 返回⼆进制结果
.text 返回编码解析的结果
.encoding 定义编码
.cookie 获取请求后的cookie
.url 获取请求网址
.json() 返回字典,可能抛出异常
.raw 返回原始socket response,需要加参数stream=True

content和text区别

content中间存的是字节码,而text中存的是Beautifulsoup模块根据猜测的编码方式将content内容编码成字符串后的结果。

直接输出content,会发现前面存在b’这样的标志,这是字节字符串的标志,而text是没有前面的b,对于纯ascii码,这两个可以说一模一样,对于其他的文字,需要正确编码才能正常显示。

有中文的时候,直接使用text会显示乱码,所以需要使用content对象来手动进行解码后才能正常显示。

响应状态码

分类 分类描述
1** 信息,服务器收到请求,需要请求者继续执⾏操作
2** 成功,操作被成功接收并处理
3** 重定向,需要进⼀步的操作以完成请求
4** 客户端错误,请求包含语法错误或⽆法完成请求
5** 服务器错误,服务器在处理请求的过程中发⽣了错误

GET请求

requests.get()

不带参数的请求

#get请求
res = requests.get("http://www.baidu.com")

print(r.status_code)
print(r.text)
print(r.content)
print(r.json())

带参数的请求

#将参数name和age拼接到URL中进行请求
res = requests.get("http://httpbin.org/getname=gemey&age=22")
#输出返回对象的文本结果
print(res.text)
#将参数name和age定义到字典params中
params={
	"name":"tony",
	"age":20
}
#发送请求参数
res = requests.get("http://httpbin.org/get",params=params)
#输出返回对象的文本结果
print(res.text)
# 定义HTTP头信息,cookie,UA和referer
headers = {
    "User-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/116.0.0.0 Safari/537.36",
    "referer": "https://www.abidu.com",
    "Cookies": "1234565678"
}
# 发送请求参数
res = requests.get("http://httpbin.org/get",headers = headers)
# 输出返回对象的文本结果
print(res.text)

POST请求

requests.post():POST请求一般用于提交参数,所以直接进行有参数的POST请求测试。

import requests

# 将参数name和age定义到字典params中
params = {
	"name": "tony",
	"age": 20
}

url = 'http://httpbin.org/post'

# 定义HTTP头信息
headers = {
    "User-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,
    "Cookies": "1234565678"
}
# 发送请求参数
res = requests.post(url = url,params = params,headers = headers)
# 输出返回对象的文本结果
print(res.text)

BeautifulSoup包

Beautiful Soup是一个用于从HTML和XML文件中提取数据的Python模块。

from bs4 import BeautifulSoup
import lxml

解释器

soup=beautifulsoup(解析内容,解析器)
解析器 使用方法 优势 劣势
Python标准库 BeautifulSoup(markup,"html.parser") Python的内置标准库、执行速度适中、文档容错能力强 Python 2.7.3 or 3.2.2)前 的版本中文档容错能力差
lxml HTML 解析器 BeautifulSoup(markup,“lxml”) 速度快、文档容错能力强 需要安装C语言库
lxml XML 解析器 BeautifulSoup(markup,["lxml-xml"])BeautifulSoup(markup,"xml") 速度快、唯一支持XML的解析器 需要安装C语言库
html5lib BeautifulSoup(markup,"html5lib") 最好的容错性、以浏览器的方式解析文档、生成HTML5格式的文档 速度慢、不依赖外部扩展

用法

#获取Tag,通俗点就是HTML中的一个个标签
soup.title                    # 获取整个title标签字段
soup.title.name               # 获取title标签名称  
soup.title.parent.name        # 获取 title 的父级标签名称
soup.p                        # 获取第一个p标签字段
soup.p['class']               # 获取第一个p中class属性值
soup.p.get('class')           # 等价于上面
soup.a                        # 获取第一个a标签字段
soup.find_all('a')            # 获取所有a标签字段
soup.find(id="link3")         # 获取属性id值为link3的字段
soup.a['class'] = "newClass"  # 可以对这些属性和内容等等进行修改
del bs.a['class']             # 还可以对这个属性进行删除
soup.find('a').get('id')      # 获取class值为story的a标签中id属性的值
soup.title.string             # 获取title标签的值

1、获取拥有指定属性的标签

方法一:获取单个属性
soup.find_all('div',id="even")            # 获取所有id=even属性的div标签
soup.find_all('div',attrs={'id':"even"})    # 效果同上

方法二:
soup.find_all('div',id="even",class_="square")            # 获取所有id=even并且class=square属性的div标签
soup.find_all('div',attrs={"id":"even","class":"square"})    # 效果同上

2、获取标签的属性值

#方法一:通过下标方式提取
for link in soup.find_all('a'):
    print(link['href'])        //等同于 print(link.get('href'))

#方法二:利用attrs参数提取
for link in soup.find_all('a'):
    print(link.attrs['href'])

3、获取标签中的内容

divs = soup.find_all('div')        # 获取所有的div标签
for div in divs:                   # 循环遍历div中的每一个div
    a = div.find_all('a')[0]      # 查找div标签中的第一个a标签     
    print(a.string)              # 输出a标签中的内容

如果结果没有正确显示,可以转换为list列表

4、stripped_strings

divs = soup.find_all('div')
for div in divs:
    infos = list(div.stripped_strings)        # 去掉空格换行等
    bring(infos)

5.输出

# 格式化输出
soup = BeautifulSoup(markup)
print(soup.prettify())

#get_text()输出
soup.get_text()
soup.i.get_text()

案例:爬取豆瓣电影Top250

import requests
from bs4 import BeautifulSoup

class Douban:
    def __init__(self):
        self.URL = 'https://movie.douban.com/top250'
        self.start_num = []
        for start_num in range(0, 251, 25):
            self.start_num.append(start_num)
        self.header = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/79.0.3945.130 Safari/537.36'}

    def get_top250(self):
        for start in self.start_num:
            start = str(start)
            html = requests.get(self.URL, params={'start':start}, headers=self.header)
            soup = BeautifulSoup(html.text, 'lxml')
            names = soup.select('#content > div > div.article > ol > li > div > div.info > div.hd > a > span:nth-child(1)')
            for name in names:
                print(name.get_text())

if __name__ == "__main__":
    cls = Douban()
    cls.get_top250()

lxml包

from lxml import etree

初始化

text = '''
<body>
    <div class="hello">这是测试的div</div>
    <div>
        <div class="hello,baby">
            这是嵌套的div标签
            <p>
                这是嵌套的p标签
            </p>
        </div>
    </div>
    <p>这是测试的p</p>
</body>
'''

html = etree.HTML(text)
#text:html内容的字符串
#html:一个lxml库的对象

result = etree.tostring(html)  #传入的不是完整结构,但是lxml会在一定程度上完善我们的html代码,尽量使它变为标准的结构。

语法

1.寻找节点

语法 含义
nodename(节点名字) 直接根据写的节点名字查找节点,如:div
// 在当前节点下的子孙节点中寻找,如://div
/ 在当前节点下的子节点中寻找,如:/div
. 代表当前节点(可省略不写,就像我们有时候写的相对路径),如:./div
当前节点的父节点,如:…/div
result = html.xpath('//div/text()') #使用xpath语法,一是在子孙节点中寻找,二是寻找div的标签
print(result)

result = html.xpath('/html/body/div/text()')
print(result)

2.属性筛选

方法名\符号 作用
@ 获取属性或者筛选属性,如:@class
contains 判断属性中是否含有某个值(用于多值判断),如:contains(@class,‘hello’)
# 筛选出class="hello"的div标签
hello_tag = html.xpath('//div[@class="hello"]')  # 注意筛选的方法都是在中括号里面的
print(hello_tag)  # 结果为: [<Element div at 0x19066c3a500>],即找到了一个标签,符合条件

#筛选出具有class="hello"的div标签
hello_tags = html.xpath('//div[contains(@class,"hello")]')
print(hello_tags) #结果为:[<Element div at 0x133c345a500>,<Element div at 0x133c345a840>],即找到了两个div标签,符合条件

3.按序选择

方法 作用
last() 获取最后一个标签
1 获取第一个标签
position() < = > num 筛选多个标签
from lxml import etree

text = '''
<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
</ul>     
'''

#初始化
html = etree.HTML(text)

#获取第一个li标签
first_tag = html.xpath('//li[1]') #令人吃惊,lxml并没有first()方法
print(first_tag)

#获取最后一个li标签
last_tag = html.xpath('//li[last()]')
print(last_tag)

#获取前五个标签
li_tags = html.xpath('//li[position() < 6]')
print(li_tags)

#获取第一个和第二个标签,使用or
tags = html.xpath('//li[position() = 1 or position() = 2]')
print(tags)

4.获取属性和文本

方法 作用
@ 获取属性或者筛选属性
text() 获取文本
#获取第div的class属性
div_class = html.xpath('//div/@class')
print(div_class)  #结果为:['hello','hello,baby']

# 获取第一个div中的p标签中的文本
content = html.xpath('//div/p/text()')  # 注意使用text()的时机和位置
print(content)  # 结果为:['这是嵌套的p标签'],仍然是以列表形式返回结果

# 获取拥有第二个div中的文本,注意观察下面的不同之处
content_two = html.xpath('//div[position() = 2]/text()')
print(content_two)  # 结果为: ['\n        ','\n    ']

content_three = html.xpath('//div[position() = 2]//text()')
print(content_three)  # 结果为: ['\n        ','\n            这是嵌套的div标签\n            ','这是嵌套的p标签','\n        ','\n    ']

# 两者不同之处在于:一个为//,一个为/。//获取其子孙节点中的内容,而/只获取其子节点的内容。

5.节点修饰语法

路径表达式 结果
//title[@lang=“eng”] 选择lang属性值为eng的所有title元素
/bookstore/book[1] 选取属于 bookstore 子元素的第一个 book 元素。
/bookstore/book[last()] 选取属于 bookstore 子元素的最后一个 book 元素。
/bookstore/book[last()-1] 选取属于 bookstore 子元素的倒数第二个 book 元素。
/bookstore/book[position()>1] 选择bookstore下面的book元素,从第二个开始选择
//book/title[text()=‘Harry Potter’] 选择所有book下的title元素,仅仅选择文本为Harry Potter的title元素
/bookstore/book[price>35.00]/title 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。

关于xpath的下标

  • 在xpath中,第一个元素的位置是1
  • 最后一个元素的位置是last()
  • 倒数第二个是last()-1

爬虫实战——静态网页

爬取某配乐网站mp3音效

import os.path
import random
import time
import requests
import lxml.etree

page_n = int(input("请输入你想要爬取的网页数量"))

for i in range(page_n):
    url = f'https://www.tuke88.com/yinxiao/zonghe_0_{i}.html'
    response = requests.get(url)

    #解析器:帮助修复html中的语法错误
    html_parser = lxml.etree.HTMLParser()
    html = lxml.etree.fromstring(response.text,parser=html_parser)
    titles = html.xpath("//div[@class='lmt']//div[@class='audio-list']//a[@class='title']/text()")
    mp3_urls = html.xpath("//div[@class='lmt']//div[@class='audio-list']//source/@src")
    if not os.path.exists('pymp3'):
        os.mkdir('pymp3')
    for title,mp3_url in zip(titles,mp3_urls):
        mp3_stream = requests.get(mp3_url,stream=True)
        with open(os.path.join('pymp3',title+".mp3"),'wb+') as writer:
            writer.write(mp3_stream.raw.read())
            print(f'【INFO】{title}.mp3下载成功')
            time.sleep(random.uniform(0.1,0.4))

原文地址:https://blog.csdn.net/weixin_44319595/article/details/132982810

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


文章浏览阅读2.4k次。最近要优化cesium里的热力图效果,浏览了网络上的各种方法,发现大多是贴在影像上的。这么做好是好,但是会被自生添加的模型或者其他数据给遮盖。其次是网上的方法大多数是截取成一个矩形。不能自定义的截取自己所需要的。经过尝试,决定修改下cesium heatmap,让他达到我们需要的要求。首先先下载 cesium heatmap包。其中我们可以看到也是通过叠加entity达到添加canvas的方法绘制到地图上。我们先把这一段代码注释} else {} };
文章浏览阅读1.2w次,点赞3次,收藏19次。在 Python中读取 json文件也可以使用 sort ()函数,在这里我介绍一个简单的示例程序: (4)如果我们想将字符串转换为列表形式,只需要添加一个变量来存储需要转换的字符串即可。在上面的代码中,我们创建了一个名为` read`的对象,然后在文件的开头使用`./`关键字来命名该对象,并在文件中定义了一个名为` json`的变量,并在其中定义了一个名为` json`的字段。比如,我们可以使用 read方法读取 json文件中的内容,然后使用 send方法将其发送到 json文件中。_python怎么读取json文件
文章浏览阅读1.4k次。首字母缩略词 API 代表应用程序编程接口,它是一种设备,例如用于使用编程代码发送和检索数据的服务器。最常见的是,该技术用于从源检索数据并将其显示给软件应用程序及其用户。当您访问网页时,API 的工作方式与浏览器相同,信息请求会发送到服务器,如何在 Windows PC 中手动创建系统还原点服务器会做出响应。唯一的区别是服务器响应的数据类型,对于 API,数据是 JSON 类型。JSON 代表 JavaScript Object Notation,它是大多数软件语言中 API 的标准数据表示法。_api是什么 python
文章浏览阅读802次,点赞10次,收藏10次。解决一个JSON反序列化问题-空字符串变为空集合_cannot coerce empty string ("") to element of `java.util.arraylist
文章浏览阅读882次。Unity Json和Xml的序列化和反序列化_unity json反序列化存储换行
文章浏览阅读796次。reader.readAsText(data.file)中data.file的数据格式为。使用FileReader对象读取文件内容,最后将文件内容进行处理使用。_a-upload 同时支持文件和文件夹
文章浏览阅读775次,点赞19次,收藏10次。fastjson是由国内的阿里推出的一种json处理器,由java语言编写,无依赖,不需要引用额外的jar包,能直接运行在jdk环境中,它的解析速度是非常之快的,目前超过了所有json库。提示:以下是引用fastjson的方法,数据未涉及到私密信息。_解析器用fastjson还是jackson
文章浏览阅读940次。【Qt之JSON文件】QJsonDocument、QJsonObject、QJsonArray等类介绍及使用_使用什么方法检查qjsondocument是否为空
文章浏览阅读957次,点赞34次,收藏22次。主要内容原生 ajax重点重点JSON熟悉章节目标掌握原生 ajax掌握jQuery ajax掌握JSON第一节 ajax1. 什么是ajaxAJAX 全称为,表示异步的Java脚本和Xml文件,是一种异步刷新技术。2. 为什么要使用ajaxServlet进行网页的变更往往是通过请求转发或者是重定向来完成,这样的操作更新的是整个网页,如果我们只需要更新网页的局部内容,就需要使用到AJAX来处理了。因为只是更新局部内容,因此,Servlet。
文章浏览阅读1.4k次,点赞45次,收藏13次。主要介绍了JsonFormat与@DateTimeFormat注解实例解析,文中通过示例代码介绍的非常详细,对大家的学习 或者工作具有一定的参考学习价值,需要的朋友可以参考下 这篇文章主要介绍了从数据库获取时间传到前端进行展示的时候,我们有时候可能无法得到一个满意的时间格式的时间日期,在数据库中显 示的是正确的时间格式,获取出来却变成了时间戳,@JsonFormat注解很好的解决了这个问题,我们通过使用 @JsonFormat可以很好的解决:后台到前台时间格式保持一致的问题,
文章浏览阅读1k次。JsonDeserialize:json反序列化注解,作用于setter()方法,将json数据反序列化为java对象。可以理解为用在处理接收的数据上。_jsondeserialize
文章浏览阅读2.7k次。labelme标注的json文件是在数据标注时产生,不能直接应用于模型训练。各大目标检测训练平台或项目框架均有自己的数据格式要求,通常为voc、coco或yolo格式。由于yolov8项目比较火热,故此本博文详细介绍将json格式标注转化为yolo格式的过程及其代码。_labelme json 转 yolo
文章浏览阅读790次,点赞26次,收藏6次。GROUP_CONCAT_UNORDERED(): 与GROUP_CONCAT类似,但不保证结果的顺序。COUNT_DISTINCT_AND_ORDERED(): 计算指定列的不同值的数量,并保持结果的顺序。COUNT_ALL_DISTINCT(): 计算指定列的所有不同值的数量(包括NULL)。AVG_RANGE(): 计算指定列的最大值和最小值之间的差异的平均值。JSON_OBJECT(): 将结果集中的行转换为JSON对象。COUNT_DISTINCT(): 计算指定列的不同值的数量。_mysql json 聚合
文章浏览阅读1.2k次。ajax同步与异步,json-serve的安装与使用,node.js的下载_json-serve 与node版本
文章浏览阅读1.7k次。`.net core`提供了Json处理模块,在命名空间`System.Text.Json`中,下面通过顶级语句,对C#的Json功能进行讲解。_c# json
文章浏览阅读2.8k次。主要介绍了python对于json文件的读写操作内容_python读取json文件
文章浏览阅读770次。然而,有时候在处理包含中文字符的Json数据时会出现乱码的情况。本文将介绍一种解决Json中文乱码问题的常见方法,并提供相应的源代码和描述。而某些情况下,中文字符可能会被错误地编码或解码,导致乱码的出现。通过适当地控制编码和解码过程,我们可以有效地处理包含中文字符的Json数据,避免乱码的发生。通过控制编码和解码过程,我们可以确保Json数据中的中文字符能够正确地传输和解析。为了解决这个问题,我们可以使用C#的System.Text.Encoding类提供的方法进行编码和解码的控制。_c# json 中文编码
文章浏览阅读997次。【代码】【工具】XML和JSON互相转换。_xml 转json
文章浏览阅读1.1k次。json path 提取数据_jsonpath数组取值
文章浏览阅读3w次,点赞35次,收藏36次。本文主要介绍了pandas read_json时ValueError: Expected object or value的解决方案,希望能对学习python的同学们有所帮助。文章目录1. 问题描述2. 解决方案_valueerror: expected object or value