文章目录
一、XPath
XPath 全称 XML Path Language,即 XML 路径语言,它是一门 XML 文档中查找信息的语言。它最初是用来搜寻 XML 文档的,但是它同样适用于 HTML 文档的搜索。
w3c官方教程
1.常用规则与安装
XPath常用规则
给出一个实例:
//title[@lang='eng']
这个匹配代表的是选择所有名称为title,同时属性值为lang的值为eng的节点,我主要是使用python的lxml库。
使用pip install lxml
引入相应的包
python简单的处理实例
from lxml import etree
# 1:在python代码中解析html字符串
text = '一个html文本'
resHtml = etree.HTML(text)
# 2:读取一个html文件进行解析
html = etree.parse('./test.html', etree.HTMLParser())
result = etree.tostring(html)
print(result.decode('utf-8'))
2.XPath节点
在 XPath 中,有七种类型的节点:元素、属性、文本、命名空间、处理指令、注释以及文档(根)节点。XML 文档是被作为节点树来对待的。树的根被称为文档节点或者根节点。
一般比较重要的时元素,属性,文本节点。这三个也是我们爬虫中处理的比较多的,元素节点也就是 title
,author
这些闭合的标签,属性节点比如lang
,文本节点就是标签中填充的文本信息了 2005,29.99
这些。属性节点比如a标签中的href
调用时需要用到@
作为标记,比如/a/@href
,但是文本标签的调用是像函数一样/a/text()
获取文本节点,后面实战部分会有~
<?xml version="1.0" encoding="ISO-8859-1"?>
<bookstore>
<book>
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
</bookstore>
<bookstore> (文档节点)
<author>J K. Rowling</author> (元素节点)
lang="en" (属性节点)
3.使用实例
首先我们需要一个html格式的文件
以下是我从我的博客格式化的一小部分源代码,我们主要熟练的解析出里面想要的东西就很可以了(ps:随便复制一段代码,在网上搜html格式化工具即可格式化成标准格式)
<!DOCTYPE html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="canonical" href="https://blog.csdn.net/csyifanZhang/article/details/105279632" />
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta name="renderer" content="webkit" />
<meta name="force-rendering" content="webkit" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="report" content="{"pid":"blog"}" />
<meta name="referrer" content="always" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
<link rel="alternate" media="handheld" href="#" />
<meta name="shenma-site-verification" content="5a59773ab8077d4a62bf469ab966a63b_1497598848" />
<meta name="csdn-baidu-search" content="{"autorun":true,"install":true,"keyword":"看完这个系列所有爬虫都easy!(一)爬虫介绍与request库使用_Python_csyifanZhang的博客-CSDN博客"}" />
<link href="https://csdnimg.cn/public/favicon.ico" rel="SHORTCUT ICON" />
<title>看完这个系列所有爬虫都easy!(一)爬虫介绍与request库使用_Python_csyifanZhang的博客-CSDN博客</title>
<meta name="description" content="文章目录1:爬虫的流程介绍2:Request的基本使用1. request()方法的参数说明2. RPython" />
<script src="//g.csdnimg.cn/tingyun/1.8.3/blog.js" type="text/javascript"></script>
<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/detail-f27ec51f6e.min.css" />
<script type="application/ld+json">{"@context":"https:\/\/ziyuan.baidu.com\/contexts\/cambrian.jsonld","@id":"https:\/\/blog.csdn.net\/csyifanZhang\/article\/details\/105279632","appid":1638831770136827,"title":"\u770b\u5b8c\u8fd9\u4e2a\u7cfb\u5217\u6240\u6709\u722c\u866b\u90fdeasy\uff01\uff08\u4e00\uff09\u722c\u866b\u4ecb\u7ecd\u4e0erequest\u5e93\u4f7f\u7528_Python_csyifanZhang\u7684\u535a\u5ba2-CSDN\u535a\u5ba2","pubDate":"2020-04-02T23:47:09","upDate":"2020-04-02T23:47:09"}</script>
<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/themes/skin-numberfifty/skin-numberfifty-8b41b58ee9.min.css" />
</head>
<body>
<ol>
<li>确认需求:你需要什么东西?
<s>
你需要美女的图片
</s></li>
<li>寻找需求:哪里能找到你想要的东西?
<s>
百度图库
</s></li>
<li>发送请求:发送访问页面的请求,获取页面的源代码。</li>
<li>解析数据:从冗杂的源代码当中提取我们需要的信息。</li>
</ol>
</body>
</html>
我们来简单分析一下上面的html,html
标签叫做根节点,他有两个孩子节点head,body
,head
中的子节点是根节点的孙子节点,层层叠带迭代。然后呢如果我现在要获取body
中所有的li
项目的文本,我因该如何做?
#使用etree解析html字符串
html = etree.HTML(text)
#解析数据:从根节点开始不断向下递归
#到li的时候吗我们已经找到了这个元素,然后就要指定我们需要的是li的文本节点
r = html.xpath('/html/body/ol/li/text()')
print(r)
可以看到,通过不断的调整你需要的节点,我们就可以获取想要的信息,当然文本信息是获取最多的。
['确认需求:你需要什么东西?\n ',
'寻找需求:哪里能找到你想要的东西?\n ',
'发送请求:发送访问页面的请求,获取页面的源代码。',
'解析数据:从冗杂的源代码当中提取我们需要的信息。']
所以简单的一修改我们就可以获取文章的title。
r = html.xpath('/html/head/title/text()')
print(r)
4.实战:爬取自己博客的所有标题,阅读量
第一步,获取网址的信息
import requests as req
from lxml import etree
#自己博客的地址
url = "https://blog.csdn.net/csyifanZhang/article/list/1"
#定义请求头信息
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36 FS',
}
res = req.get(url, headers = headers)
if res.status_code == 200:
#写入文件,你可能会编码出错,这时候记着一定要加encoding属性
with open('./test.html', 'w', encoding = 'utf-8') as fp:
fp.write(res.text)
第二步,解析数据,观察想要的数据的标签所在位置
# 解析数据
html = etree.parse('./test.html',etree.HTMLParser())
# 提取标题,阅读量+评论数 文章地址url
# //双斜杠表示选取子孙节点,具体含义上面有
title = html.xpath("//div[@class='article-list']//h4/a/text()")
unwatch = html.xpath("//div[@class='article-list']//span[@class='read-num']/span[@class='num']/text()")
url = html.xpath("//div[@class='article-list']//a/@href")
r1 = []
r2 = []
data = []
for i in range(int(len(unwatch)/2)):
r2.append({"阅读量" :unwatch[2*i], "评论量:": unwatch[2*i+1]})
print(len(title))
for i in range(len(title)):
if len(title[i]) > 10:
print(title[i])
print(len(title[i]))
r1.append(title[i])
for i in range(len(r1)-1):
res = {'title':r1[i], '访问情况':r2[i],"url": url[i]}
data.append(res)
第三步,存储数据:
with open('./data.json', 'w') as fp:
json.dump(self.data,fp)
最后,我们把他封装成一个类,然后实例化对象进行测试:
# -*- coding: utf-8 -*-
"""
Created on Fri Apr 3 11:48:27 2020
@author: csyfZhang
"""
import requests as req
import json
from lxml import etree
class crawl():
#自己博客的地址
url = "https://blog.csdn.net/csyifanZhang/article/list/1"
#定义请求头信息
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36 FS',
}
#存储爬取的数据
data = ''
# 存储到json文件
filePath = './text.json'
def __init__(self):
res = req.get(self.url, headers = self.headers)
if res.status_code == 200:
#写入文件
with open('./test.html', 'w', encoding = 'utf-8') as fp:
fp.write(res.text)
if self.pasedata():
self.writedata()
print("写入成功--------------------------------------------")
def pasedata(self):
print("正在解析-------------------------------------------")
# 解析数据
html = etree.parse('./test.html',etree.HTMLParser())
# 提取标题,阅读量+评论数 文章地址url
# //双斜杠表示选取子孙节点,具体含义上面有
title = html.xpath("//div[@class='article-list']//h4/a/text()")
unwatch = html.xpath("//div[@class='article-list']//span[@class='read-num']/span[@class='num']/text()")
url = html.xpath("//div[@class='article-list']//a/@href")
r1 = []
r2 = []
data = []
for i in range(int(len(unwatch)/2)):
r2.append({"阅读量" :unwatch[2*i], "评论量:": unwatch[2*i+1]})
for i in range(len(title)):
if len(title[i]) > 10:
r1.append(title[i])
for i in range(len(r1)-1):
res = {'title':r1[i], '访问情况':r2[i],"url": url[i]}
data.append(res)
self.data = data
return True
def writedata(self):
print("正在写入--------------------------------------")
with open('./data.json', 'w') as fp:
json.dump(self.data,fp)
# 创建实例化对象
crawl()
部分结果展示:
[{‘title’: ’ 看完这个系列所有爬虫都easy!(一)爬虫介绍与request库使用 ', ‘访问情况’:
{‘阅读量’: ‘566’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105279632’},
{‘title’: ’ Mobile Edge Computing学习笔记(二)问题,挑战与主流研究方向 ',
‘访问情况’: {‘阅读量’: ‘204’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105279632’},
{‘title’: ’ Mobile Edge Computing学习笔记(一)从通信领域看移动边缘计算:现有的经典结构与模型
', ‘访问情况’: {‘阅读量’: ‘324’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105118127’},
{‘title’: ’ Federated Machine Learning学习笔记(二):区块链技术 ',
‘访问情况’: {‘阅读量’: ‘201’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105118127’},
{‘title’: ’ 网络信息检索(一)检索模型:布尔,向量,概率检索 ', ‘访问情况’: {‘阅读量’:
‘305’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105074521’},
{‘title’: ’ 初识python爬虫:5分钟爬取博客的全部信息 ', ‘访问情况’: {‘阅读量’:
‘23’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105074521’},
{‘title’: ’ 深入浅出:四种常用的最短路算法+两种常用生成树算法+网络流常用算法大礼包 ',
‘访问情况’: {‘阅读量’: ‘20’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/104691745’},
{‘title’: ’ 3259 :Wormholes:spfa+bellman-ford如何判断负环 ',
‘访问情况’: {‘阅读量’: ‘22’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/104691745’},
{‘title’: ’ LuoGu p4702 取石子:入门级博弈 ', ‘访问情况’: {‘阅读量’: ‘39’,
‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/104656408’},
{‘title’: ’ 1067:取石子游戏:威佐夫博奕详细推导 ', ‘访问情况’: {‘阅读量’: ‘27’,
‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/104656408’},
{‘title’: ’ ACM 关于博弈问题,看这里就够了! ', ‘访问情况’: {‘阅读量’: ‘12’,
‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105276795’},
{‘title’: ’ 1936 All in All:超喜欢的模板题:字符串t能否认s做父 ', ‘访问情况’:
{‘阅读量’: ‘11’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105276795’},
{‘title’: ’ 1503 Integer Inquiry:超长数加法 坑点分析+友情提示 ',
‘访问情况’: {‘阅读量’: ‘9’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105269356’},
{‘title’: ’ 2823 :Sliding Window:滑动窗口问题:单调队列原理+模板+使用~ ',
‘访问情况’: {‘阅读量’: ‘10’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105269356’},
{‘title’: ’ 1316:Self Numbers: 类埃及筛法,换种思路,生活如此简单 ',
‘访问情况’: {‘阅读量’: ‘16’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105266973’},
{‘title’: ’ 1068 Parencodings:小数据就是可以为所欲为,暴力模拟,0msKO ',
‘访问情况’: {‘阅读量’: ‘24’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105266973’},
{‘title’: ’ 2017 Speed Limit:0ms AC的小水题 ', ‘访问情况’: {‘阅读量’:
‘20’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105266469’},
{‘title’: ’ 1459 Power Network:阅读比题恶心系列:网络流怎么流+坑点分析+一组样例数据
', ‘访问情况’: {‘阅读量’: ‘20’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105266469’},
{‘title’: ’ 1328Radar Installation:如何贪心+坑点记录+一组样例数据 ',
‘访问情况’: {‘阅读量’: ‘25’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105262768’},
{‘title’: ’ 网络信息检索(五)查询处理:查询方式+查询操作 ', ‘访问情况’: {‘阅读量’:
‘57’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105262768’},
{‘title’: ’ 嵌入式系统(七):关于串行通信你不得不知的概念 ', ‘访问情况’: {‘阅读量’:
‘21’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105263021’},
{‘title’: ’ 2524:Ubiquitous Religions:宗教信仰:并查集简单使用+并查集基础附送
', ‘访问情况’: {‘阅读量’: ‘20’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105263021’},
{‘title’: ’ 1979:Red and Black:经典地图遍历,dfs强行跑图+坑点分析 ',
‘访问情况’: {‘阅读量’: ‘16’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105262371’},
{‘title’: ’ 2104:K-th
Number:以题为例,一文搞懂主席树的原理+代码(我画了一天图,就是为了你能看懂!) ', ‘访问情况’: {‘阅读量’:
‘21’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105262371’},
{‘title’: ’ 1298:The Hardest Problem Ever:string类快速处理字符转换
', ‘访问情况’: {‘阅读量’: ‘35’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105259174’},
{‘title’: ’ 1013 Counterfeit
Dollar:我们不要暴力:优美的确定假币(内含一大组数据+各种情况分析) ', ‘访问情况’: {‘阅读量’: ‘31’,
‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105259174’},
{‘title’: " 2528:Mayor’s posters:为什么要线段树?为什么要离散化?一文搞懂该题 ",
‘访问情况’: {‘阅读量’: ‘19’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105257494’},
{‘title’: ’ 计算方法:三次样条插值原理 ', ‘访问情况’: {‘阅读量’: ‘44’, ‘评论量:’:
‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105257494’},
{‘title’: ’ 这可能是我见过最详细的线段树教程(基础+进阶) ', ‘访问情况’: {‘阅读量’:
‘56’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105256813’},
{‘title’: ’ 1028:Web Navigation:堆栈操作+小坑点分析 ', ‘访问情况’:
{‘阅读量’: ‘21’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105256813’},
{‘title’: ’ 2000 Gold Coins(金币):模拟打表,小水题 ', ‘访问情况’:
{‘阅读量’: ‘16’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105255775’},
{‘title’: ’ 史上最全的http状态码分类与解析 ', ‘访问情况’: {‘阅读量’: ‘28’,
‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105255775’},
{‘title’: ’ GET,POST,PUT,DELETE,OPTIONS等请求方式简单总结 ',
‘访问情况’: {‘阅读量’: ‘40’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105255537’},
{‘title’: ’ Mobile Edge Computing学习笔记(三)MEC用例与offloading研究综述
', ‘访问情况’: {‘阅读量’: ‘83’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105255537’},
{‘title’: ’ 1753 Flip Game:状态压缩DP翻转4*4期棋盘 0ms通关 ', ‘访问情况’:
{‘阅读量’: ‘24’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105254056’},
{‘title’: ’ 2406: Power Strings:另辟新径:easy快速幂判断字符乘方 ',
‘访问情况’: {‘阅读量’: ‘24’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105254056’},
{‘title’: ’ 2503 Babelfish:字符串string类+map操作总结 ', ‘访问情况’:
{‘阅读量’: ‘25’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105252544’},
{‘title’: " 2262:Goldbach’s Conjecture:埃及筛法+尺取 证明哥德巴赫猜想 ",
‘访问情况’: {‘阅读量’: ‘31’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105252544’},
{‘title’: ’ 2352 Stars:数个星星都这么麻烦!如何从暴搜进阶到树状数组 ', ‘访问情况’:
{‘阅读量’: ‘14’, ‘评论量:’: ‘0’}, ‘url’:
‘https://blog.csdn.net/csyifanZhang/article/details/105244813’}]
二、bs4的安装与三种使用方式
安装
pip install beautifulsoup4
1.通过标签访问各级元素
首先我们定义下面的变量,引入相应的包
from bs4 import BeautifulSoup
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><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>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""
通过标签访问其实很简单,
首先我们要炖汤,创建一个锅,然后不断的从汤里拿出自己想吃的菜。不过需要注意的是内置的属性class,href
需要使用[]
访问,而文本text则是直接使用.
操作,这一点和XPath有点类似。
# 创建一个beautifulsoup对象,建议手动指定解析器:soup = BeautifulSoup(html_doc, 'lxml')
soup = BeautifulSoup(html_doc, 'lxml')
# 1:通过tag标签获取文档数据
r = soup.title #result: <title>The Dormouse's story</title>
r = soup.p #只能获取第一个匹配的元素,这里也就是第一个p标签
r = soup.p['class'] # 获取第一个匹配元素的class
r = soup.title.text #获取对象的文本
r = soup.a['href'] # 获取a对象的href属性
2.通过搜索获取页面元素
在这里我们主要用两种函数,find,find_all
,他们有什么差别呢?
# 2:通过搜索获取页面的元素 find,find_all
r = soup.find('a') #只获取第一个匹配的标签
text = r.get_text() #只可见文本内容
text = r.text #和上面的操作结果一样
r = soup.find_all('a') #获取所有匹配标签
3.css选择器
对于有前端开发经验,对css选择器比较熟悉的,用这个还是不错的。可以通过标签选择元素,也可以通过class类名获取元素,也可以通过id名获取元素,也可以通过层级关系获取元素。
# 3:通过css选择器获取元素
#通过标签获取元素
r = soup.select('title') #返回值是所有title标签的列表
#通过class类名获取元素
r = soup.select('.story') #注意以.开头表示类名
#通过id获取元素
r = soup.select('#xxx') # 注意以#开头表示id