更新了部分文档
Before Width: | Height: | Size: 177 KiB After Width: | Height: | Size: 177 KiB |
After Width: | Height: | Size: 456 KiB |
After Width: | Height: | Size: 723 KiB |
After Width: | Height: | Size: 1.3 MiB |
After Width: | Height: | Size: 225 KiB |
Before Width: | Height: | Size: 200 KiB After Width: | Height: | Size: 200 KiB |
|
@ -134,10 +134,10 @@ while m:
|
|||
```Python
|
||||
import re
|
||||
|
||||
sentence = '你丫是傻逼吗? 我操你大爷的. Fuck you.'
|
||||
purified = re.sub('[操草艹]|fuck|shit|[傻煞沙][比屄逼叉缺吊屌碉雕]',
|
||||
sentence = 'Oh, shit! 你丫是傻叉吗? Fuck you.'
|
||||
purified = re.sub('fuck|shit|[傻煞沙][比屄逼叉缺吊屌碉雕]',
|
||||
'*', sentence, flags=re.IGNORECASE)
|
||||
print(purified) # 你丫是*吗? 我*你大爷的. * you.
|
||||
print(purified) # Oh, *! 你丫是*吗? * you.
|
||||
```
|
||||
|
||||
> **说明:**` re`模块的正则表达式相关函数中都有一个`flags`参数,它代表了正则表达式的匹配标记,可以通过该标记来指定匹配时是否忽略大小写、是否进行多行匹配、是否显示调试信息等。如果需要为flags参数指定多个值,可以使用[按位或运算符](http://www.runoob.com/python/python-operators.html#ysf5)进行叠加,如`flags=re.I | re.M`。
|
||||
|
|
|
@ -1,10 +1,137 @@
|
|||
## 第029课:用Python操作PDF文件
|
||||
|
||||
####
|
||||
PDF是Portable Document Format的缩写,这类文件通常使用`.pdf`作为其扩展名。在日常开发工作中,最容易遇到的就是从PDF中读取文本内容以及用已有的内容生成PDF文档这两个任务。
|
||||
|
||||
### 从PDF中提取文本
|
||||
|
||||
在Python中,可以使用名为`PyPDF2`的三方库来读取PDF文件,可以使用下面的命令来安装它。
|
||||
|
||||
```Bash
|
||||
pip install PyPDF2 -i https://pypi.doubanio.com/simple
|
||||
```
|
||||
|
||||
`PyPDF2`没有办法从PDF文档中提取图像、图表或其他媒体,但它可以提取文本,并将其返回为Python字符串。
|
||||
|
||||
```Python
|
||||
import PyPDF2
|
||||
|
||||
reader = PyPDF2.PdfFileReader('test.pdf')
|
||||
page = reader.getPage(0)
|
||||
print(page.extractText())
|
||||
```
|
||||
|
||||
当然,`PyPDF2`并不是什么样的PDF文档都能提取出文字来,这个问题就我所知并没有什么特别好的解决方法,尤其是在提取中文的时候。之前给成都一汽大众做企业内训的时候,就有学员提出了一个从报关信息的PDF文档中提取中文文本内容的需求,但是我尝试了多种方法都失败了。网上也有很多讲解从PDF中提取文字的文章,推荐大家自行阅读[《三大神器助力Python提取pdf文档信息》](https://cloud.tencent.com/developer/article/1395339)一文进行了解。
|
||||
|
||||
要从PDF文件中提取文本也可以使用一个三方的命令行工具,具体的做法如下所示。
|
||||
|
||||
```Bash
|
||||
pip install pdfminer.six
|
||||
pdf2text.py test.pdf
|
||||
```
|
||||
|
||||
### 旋转和叠加页面
|
||||
|
||||
上面的代码中通过创建`PdfFileReader`对象的方式来读取PDF文档,该对象的`getPage`方法可以获得PDF文档的指定页并得到一个`Page`对象,利用`Page`对象的`rotateClockwise`和`rotateCounterClockwise`方法可以实现页面的顺时针和逆时针方向旋转,代码如下所示。
|
||||
|
||||
```Python
|
||||
import PyPDF2
|
||||
|
||||
reader = PyPDF2.PdfFileReader('test.pdf')
|
||||
page = reader.getPage(0)
|
||||
page.rotateClockwise(90)
|
||||
writer = PyPDF2.PdfFileWriter()
|
||||
writer.addPage(page)
|
||||
with open('test-rotated.pdf', 'wb') as file:
|
||||
writer.write(file)
|
||||
```
|
||||
|
||||
`Page`对象还有一个名为`mergePage`的方法,可以将一个页面和另一个页面进行叠加,对于叠加后的页面,我们还是使用`PdfFileWriter`对象的`addPage`将其添加到一个新的`PDF`文档中,有兴趣的读者可以自行尝试。
|
||||
|
||||
### 加密PDF文件
|
||||
|
||||
使用`PyPDF2`中的`PdfFileWrite`对象可以为PDF文档加密,如果需要给一系列的PDF文档设置统一的访问口令,使用Python程序来处理就会非常的方便。
|
||||
|
||||
```Python
|
||||
import PyPDF2
|
||||
|
||||
with open('test.pdf', 'rb') as file:
|
||||
# 通过PdfReader读取未加密的PDF文档
|
||||
reader = PyPDF2.PdfFileReader(file)
|
||||
writer = PyPDF2.PdfFileWriter()
|
||||
for page_num in range(reader.numPages):
|
||||
# 通过PdfReader的getPage方法获取指定页码的页
|
||||
# 通过PdfWriter方法的addPage将添加读取到的页
|
||||
writer.addPage(reader.getPage(page_num))
|
||||
# 通过PdfWriter的encrypt方法加密PDF文档
|
||||
writer.encrypt('foobared')
|
||||
# 将加密后的PDF文档写入指定的文件中
|
||||
with open('test-encrypted.pdf', 'wb') as file2:
|
||||
writer.write(file2)
|
||||
```
|
||||
|
||||
> **提示**:按照上面的方法也可以读取多个PDF文件的多个页面并将其合并到一个PDF文件中。
|
||||
|
||||
### 创建PDF文件
|
||||
|
||||
创建PDF文档需要三方库`reportlab`的支持,该库的使用方法可以参考它的[官方文档](https://www.reportlab.com/docs/reportlab-userguide.pdf),安装的方法如下所示。
|
||||
|
||||
```Bash
|
||||
pip install reportlab
|
||||
```
|
||||
|
||||
下面通过一个例子为大家展示`reportlab`的用法。
|
||||
|
||||
```Python
|
||||
import random
|
||||
|
||||
from reportlab.lib import colors
|
||||
from reportlab.lib.pagesizes import A4
|
||||
from reportlab.pdfgen import canvas
|
||||
from reportlab.platypus import Table, TableStyle
|
||||
|
||||
# 创建Canvas对象(PDF文档对象)
|
||||
doc = canvas.Canvas('demo.pdf', pagesize=A4)
|
||||
# 获取A4纸的尺寸
|
||||
width, height = A4
|
||||
# 读取图像
|
||||
image = canvas.ImageReader('guido.jpg')
|
||||
# 通过PDF文档对象的drawImage绘制图像内容
|
||||
doc.drawImage(image, (width - 250) // 2, height - 475, 250, 375)
|
||||
# 设置字体和颜色
|
||||
doc.setFont('Helvetica', 32)
|
||||
doc.setFillColorRGB(0.8, 0.4, 0.2)
|
||||
# 通过PDF文档对象的drawString输出字符串内容
|
||||
doc.drawString(10, height - 50, "Life is short, I use Python!")
|
||||
# 保存当前页创建新页面
|
||||
doc.showPage()
|
||||
# 准备表格需要的数据
|
||||
scores = [[random.randint(60, 100) for _ in range(3)] for _ in range(5)]
|
||||
names = ('Alice', 'Bob', 'Jack', 'Lily', 'Tom')
|
||||
for row, name in enumerate(names):
|
||||
scores[row].insert(0, name)
|
||||
scores.insert(0, ['Name', 'Verbal', 'Math', 'Logic'])
|
||||
# 创建一个Table对象(第一个参数是数据,第二个和第三个参数是列宽和行高)
|
||||
table = Table(scores, 50, 20)
|
||||
# 设置表格样式(对齐方式和内外边框粗细颜色)
|
||||
table.setStyle(TableStyle([
|
||||
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
|
||||
('INNERGRID', (0, 0), (-1, -1), 0.25, colors.red),
|
||||
('BOX', (0, 0), (-1, -1), 0.25, colors.black)
|
||||
]))
|
||||
table.split(0, 0)
|
||||
# 通过Table对象的drawOn在PDF文档上绘制表格
|
||||
table.drawOn(doc, (width - 200) // 2, height - 150)
|
||||
# 保存当前页创建新页面
|
||||
doc.showPage()
|
||||
# 保存PDF文档
|
||||
doc.save()
|
||||
```
|
||||
|
||||
> **说明**:上面的代码使用了很多字面常量来指定位置和尺寸,在商业项目开发中应该避免这样的硬代码(hard code),因为这样的代码不仅可读性差,维护起来也是一场噩梦。如果项目中需要动态生成PDF文档且PDF文档的格式是相对固定的,可以将上面的字面常量处理成符号常量。记住:**符号常量优于字面常量**。Python语言没有内置定义常量的语法,但是可以约定变量名使用全大写字母的变量就是常量。
|
||||
|
||||
### 简单的总结
|
||||
|
||||
|
||||
在学习完上面的内容之后,相信大家已经知道像合并多个PDF文件这样的工作应该如何用Python代码来处理了,赶紧自己动手试一试吧。
|
||||
|
||||
> **温馨提示**:学习中如果遇到困难,可以加**QQ交流群**询问。
|
||||
>
|
||||
|
|
|
@ -1,10 +1,86 @@
|
|||
## 第030课:用Python获取网络资源
|
||||
## 第030课:用Python获取网络数据
|
||||
|
||||
####
|
||||
对于Python语言来说,一个较为擅长的领域就是网络数据采集,实现网络数据采集的程序通常称之为网络爬虫或蜘蛛程序。即便是在大数据时代,数据对于中小企业来说仍然是硬伤和短板,有些数据需要通过开放或付费的数据接口来获得,其他的行业数据则必须要通过网络数据采集的方式来获得。不管使用哪种方式获取网络数据资源,Python语言都是非常好的选择,因为Python的标准库和三方库都对获取网络数据提供了良好的支持。
|
||||
|
||||
### HTTP和requests库
|
||||
|
||||
要使用Python获取网络数据,我们可以先安装一个名为`requests` 的三方库,这个库我们在第24课中已经使用过了。按照官方网站的解释,`requests`是基于Python标准库进行了封装,简化了通过[HTTP](https://www.ruanyifeng.com/blog/2016/08/http.html)访问网络资源的操作。说到HTTP相信大家不会陌生,通常我们打开浏览器浏览网页时,我们就是使用了HTTP或[HTTPS](https://zhuanlan.zhihu.com/p/100591696)。HTTP是一个请求响应式的协议,当我们在浏览器中输入正确的[URL](https://developer.mozilla.org/zh-CN/docs/Learn/Common_questions/What_is_a_URL)(通常也称为网址)并按下回车(Enter),我们就向网络上的[Web服务器](https://developer.mozilla.org/zh-CN/docs/Learn/Common_questions/What_is_a_web_server)发送了一个HTTP请求,服务器在收到请求后会给我们一个HTTP响应,服务器给浏览器的数据就包含在这个响应中。我们可以使用浏览器提供的“开发者工具”或是“抓包工具”(如:Fiddler、Charles等)来了解HTTP请求和响应到底是什么样子的,如下图所示。
|
||||
|
||||
![](res/http-request-response.png)
|
||||
|
||||
通过`requests`库,我们可以让程序向浏览器一样向Web服务器发起请求,并接收到服务器返回的响应,从响应中我们就可以提取出我们想要的数据。下面通过两个例子来演示如何获取网页代码和网络资源(如:图片),浏览器呈现给我们的网页是用[HTML](https://developer.mozilla.org/zh-CN/docs/Web/HTML)编写的,浏览器相当于是HTML的解释器环境,我们看到的网页中的内容都包含在HTML的标签中。在获取到HTML代码后,就可以从标签的属性或标签体中提取我们需要的内容。
|
||||
|
||||
获取搜狐网首页。
|
||||
|
||||
```Python
|
||||
import requests
|
||||
|
||||
# requests的get函数会返回一个Response对象
|
||||
resp = requests.get('https://www.sohu.com/')
|
||||
if resp.status_code == 200:
|
||||
# 通过Response对象的text属性获取服务器返回的文本内容
|
||||
print(resp.text)
|
||||
```
|
||||
|
||||
获取百度Logo并保存到名为`baidu.png`的本地文件中。首先在百度的首页上,右键点击百度Logo,并通过“复制图片地址”菜单获取图片的URL。
|
||||
|
||||
```Python
|
||||
import requests
|
||||
|
||||
resp = requests.get('https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png')
|
||||
with open('baidu.png', 'wb') as file:
|
||||
# 通过Response对象的content属性获取服务器返回的二进制内容
|
||||
file.write(resp.content)
|
||||
```
|
||||
|
||||
> **说明**:关于`requests`库的详细使用方法,大家可以参考[官方文档](https://requests.readthedocs.io/zh_CN/latest/)的内容。
|
||||
|
||||
### 访问网络数据接口
|
||||
|
||||
国内外的很多网站都提供了开放数据接口,在开发商业项目时,如果有些事情我们自己无法解决,就可以借助这些开放的数据接口来处理。例如要根据用户或企业上传的资料进行实名认证或企业认证,我们就可以调用第三方提供的开放接口来识别用户或企业信息的真伪;例如要获取某个城市的天气信息,我们不可能直接从气象卫星拿到数据然后自己进行运算,只能通过第三方提供的数据接口来得到对应的天气信息。通常,提供有商业价值的数据的接口都是需要支付费用后才能访问的,在访问接口时还需要提供身份标识,便于服务器判断用户是不是付费用户以及进行费用扣除等相关操作。当然,还有些接口是可以免费使用的,但是必须先提供个人或者公司的信息才能访问,例如:[深圳市政府数据开放平台](https://opendata.sz.gov.cn/data/api/toApi)、[蜻蜓FM开放平台](https://open.qingting.fm/)等。如果查找自己需要的数据接口,可以访问[聚合数据](https://www.juhe.cn/)这类型的网站。
|
||||
|
||||
目前,我们访问的网络数据接口大多会返回JSON格式的数据,我们在第24课讲解序列化和反序列的时候,提到过JSON格式的字符串跟Python中的字典如何进行转换,并以[天行数据](https://www.tianapi.com/)为例讲解过网络数据接口访问的相关知识,这里我们就不再进行赘述了。
|
||||
|
||||
### 开发爬虫/蜘蛛程序
|
||||
|
||||
有的时候,我们需要的数据并不能通过开放数据接口来获得,但是可能在某些网页上能够获取到,这个时候就需要我们开发爬虫程序通过爬取页面来获得需要的内容。我们可以按照上面提供的方法,使用`requests`先获取到网页的HTML代码,我们可以将整个代码看成一个长字符串,这样我们就可以使用正则表达式的捕获组从字符串提取我们需要的内容。下面我们通过代码演示如何从[豆瓣电影](https://movie.douban.com/)获取排前250名的电影的名称。豆瓣电影Top250页面的结构和对应的代码如下图所示。
|
||||
|
||||
![](res/douban-movie-top250.png)
|
||||
|
||||
```Python
|
||||
import random
|
||||
import re
|
||||
import time
|
||||
|
||||
import requests
|
||||
|
||||
for page in range(1, 11):
|
||||
resp = requests.get(
|
||||
# 请求https://movie.douban.com/top250时,start参数代表了从哪一部电影开始
|
||||
url=f'https://movie.douban.com/top250?start={(page - 1) * 25}',
|
||||
# 如果不设置HTTP请求头中的User-Agent,豆瓣会检测出爬虫程序而阻止我们的请求
|
||||
# User-Agent可以设置为浏览器的标识(可以在浏览器的开发者工具查看HTTP请求头找到)
|
||||
# 由于豆瓣网允许百度爬虫获取它的数据,因此直接将我们的爬虫伪装成百度的爬虫
|
||||
headers={'User-Agent': 'BaiduSpider'}
|
||||
)
|
||||
# 创建正则表达式对象,通过捕获组捕获span标签中的电影标题
|
||||
pattern = re.compile(r'\<span class="title"\>([^&]*?)\<\/span\>')
|
||||
# 通过正则表达式获取class属性为title且标签内容不以&符号开头的span标签
|
||||
results = pattern.findall(resp.text)
|
||||
# 循环变量列表中所有的电影标题
|
||||
for result in results:
|
||||
print(result)
|
||||
# 随机休眠1-3秒,避免获取页面过于频繁
|
||||
time.sleep(random.randint(1, 3))
|
||||
```
|
||||
|
||||
编写爬虫程序比较重要的一点就是让爬虫程序隐匿自己的身份,因为一般的网站都比较反感爬虫。隐匿身份除了像上面的代码中修改`User-Agent`之外,还可以使用**商业IP代理**(如:[蘑菇代理](http://www.moguproxy.com/)、[芝麻代理](http://www.zhimaruanjian.com/)等),让被爬取的网站无法得知爬虫程序的真实IP地址,也就无法通过IP地址对爬虫程序进行封禁。当然,爬虫本身也是一个处于灰色地带的东西,没有谁说它是违法的,但也没有谁说它是合法的,本着**法不禁止即为许可**的精神,我们可以根据自己工作的需要去编写爬虫程序,但是如果被爬取的网站能够**举证你的爬虫程序有破坏动产的行为**,那么在打官司的时候,基本上是要败诉的,这一点需要注意。
|
||||
|
||||
另外,用编写正则表达式的方式从网页中提取内容虽然可行,但是写出一个能够满足需求的正则表达式本身也不是件容易的事情,这一点对于新手来说尤为明显。在下一节课中,我们将会为大家介绍另外两种从页面中提取数据的方法,虽然从性能上来讲,它们可能不如正则表达式,但是却降低了编码的复杂性,相信大家会喜欢上它们的。
|
||||
|
||||
### 简单的总结
|
||||
|
||||
|
||||
Python语言能做的事情真的很多,就获取网络数据这一项而言,Python几乎是一枝独秀的,大量的企业和个人都会使用Python从网络上获取自己需要的数据,相信这一点在现在或者将来也会是你工作中的一部分。
|
||||
|
||||
> **温馨提示**:学习中如果遇到困难,可以加**QQ交流群**询问。
|
||||
>
|
||||
|
|
|
@ -1,10 +1,158 @@
|
|||
## 第031课:用Python解析HTML页面
|
||||
|
||||
####
|
||||
在上一课中我们讲到了使用Python获取网络资源,如果我们获取到一个或多个页面,需要从页面中提取出指定的信息,首先得掌握解析HTML页面的技术。上一课中我们把整个HTML页面当成一个字符串,使用正则表达式的捕获组提取出了需要的内容。但是,写出一个正确的正则表达式经常也是一件让人头疼的事情。为此,我们可以先了解HTML页面的结构,在此基础上就可以掌握其他的解析HTML页面的方法。
|
||||
|
||||
### HTML页面的结构
|
||||
|
||||
我们在浏览器中打开任意一个网站,然后通过鼠标右键菜单,选择“显示网页源代码”菜单项,就可以看到网页对应的HTML代码。
|
||||
|
||||
![](res/html_page_code.png)
|
||||
|
||||
代码的第1行是文档类型声明,第2行的`<html>`标签是整个页面根标签的开始标签,最后一行是根标签的结束标签`</html>`。`<html>`标签下面有两个子标签`<head>`和`<body>`,放在`<body>`标签下的内容会显示在浏览器窗口中,这部分内容是网页的主体;放在`<head>`标签下的内容不会显示在浏览器窗口中,但是却包含了页面重要的元信息,通常称之为网页的头部。HTML页面大致的代码结构如下所示。
|
||||
|
||||
```HTML
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<!-- 页面的元信息,如字符编码、标题、关键字、媒体查询等 -->
|
||||
</head>
|
||||
<body>
|
||||
<!-- 页面的主体,显示在浏览器窗口中的内容 -->
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
标签、层叠样式表(CSS)、JavaScript是构成HTML页面的三要素,其中标签用来承载页面要显示的内容,CSS负责对页面的渲染,而JavaScript用来控制页面的交互式行为。对HTML页面的解析可以使用一种名为XPath的语法,根据HTML标签的层次结构提取标签中的内容或标签属性;除此之外,也可以使用CSS选择器来定位页面元素,如果不清楚什么是CSS选择器,可以移步到我的[《Web前端概述》](https://github.com/jackfrued/Python-100-Days/blob/master/Day21-30/21-30.Web%E5%89%8D%E7%AB%AF%E6%A6%82%E8%BF%B0.md)一文进行了解。
|
||||
|
||||
### XPath解析
|
||||
|
||||
XPath是在XML(eXtensible Markup Language)文档中查找信息的一种语法,XML跟HTML类似也是一种用标签承载数据的标签语言,不同之处在于XML的标签是可扩展的,可以自定义的,而且XML对语法有更严格的要求。XPath使用路径表达式来选取XML文档中的节点或者节点集,这里所说的节点包括元素、属性、文本、命名空间、处理指令、注释、根节点等。下面我们通过一个例子来说明如何使用XPath对页面进行解析。
|
||||
|
||||
```XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<bookstore>
|
||||
<book>
|
||||
<title lang="eng">Harry Potter</title>
|
||||
<price>29.99</price>
|
||||
</book>
|
||||
<book>
|
||||
<title lang="eng">Learning XML</title>
|
||||
<price>39.95</price>
|
||||
</book>
|
||||
</bookstore>
|
||||
```
|
||||
|
||||
对于上面的XML文件,我们可以用如下所示的XPath语法获取文档中的节点。
|
||||
|
||||
| 路径表达式 | 结果 |
|
||||
| --------------- | ------------------------------------------------------------ |
|
||||
| /bookstore | 选取根元素 bookstore。**注意**:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径! |
|
||||
| //book | 选取所有 book 子元素,而不管它们在文档中的位置。 |
|
||||
| //@lang | 选取名为 lang 的所有属性。 |
|
||||
| /bookstore/book[1] | 选取属于 bookstore 子元素的第一个 book 元素。 |
|
||||
| /bookstore/book[last()] | 选取属于 bookstore 子元素的最后一个 book 元素。 |
|
||||
| /bookstore/book[last()-1] | 选取属于 bookstore 子元素的倒数第二个 book 元素。 |
|
||||
| /bookstore/book[position()<3] | 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。 |
|
||||
| //title[@lang] | 选取所有拥有名为 lang 的属性的 title 元素。 |
|
||||
| //title[@lang='eng'] | 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。 |
|
||||
| /bookstore/book[price>35.00] | 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。 |
|
||||
| /bookstore/book[price>35.00]/title | 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。 |
|
||||
|
||||
XPath还支持通配符用法,如下所示。
|
||||
|
||||
| 路径表达式 | 结果 |
|
||||
| ------------ | --------------------------------- |
|
||||
| /bookstore/* | 选取 bookstore 元素的所有子元素。 |
|
||||
| //* | 选取文档中的所有元素。 |
|
||||
| //title[@*] | 选取所有带有属性的 title 元素。 |
|
||||
|
||||
如果要选取多个节点,可以使用如下所示的方法。
|
||||
|
||||
| 路径表达式 | 结果 |
|
||||
| -------------------------------- | ------------------------------------------------------------ |
|
||||
| //book/title \| //book/price | 选取 book 元素的所有 title 和 price 元素。 |
|
||||
| //title \| //price | 选取文档中的所有 title 和 price 元素。 |
|
||||
| /bookstore/book/title \| //price | 选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素。 |
|
||||
|
||||
> **说明**:上面的例子来自于“菜鸟教程”网站上的[XPath教程](<https://www.runoob.com/xpath/xpath-tutorial.html>),有兴趣的读者可以自行阅读原文。
|
||||
|
||||
当然,如果不理解或不熟悉XPath语法,可以在浏览器的开发者工具中按照如下所示的方法查看元素的XPath语法,下图是在Chrome浏览器的开发者工具中查看豆瓣网电影详情信息中影片标题的XPath语法。
|
||||
|
||||
![](res/douban-xpath.png)
|
||||
|
||||
实现XPath解析需要三方库`lxml` 的支持,可以使用下面的命令安装`lxml`。
|
||||
|
||||
```Bash
|
||||
pip install lxml
|
||||
```
|
||||
|
||||
下面我们用XPath解析方式改写之前获取豆瓣电影Top250的代码,如下所示。
|
||||
|
||||
```Python
|
||||
import random
|
||||
import time
|
||||
|
||||
from lxml import etree
|
||||
import requests
|
||||
|
||||
for page in range(1, 11):
|
||||
resp = requests.get(
|
||||
url=f'https://movie.douban.com/top250?start={(page - 1) * 25}',
|
||||
headers={
|
||||
'User-Agent': 'BaiduSpider',
|
||||
}
|
||||
)
|
||||
tree = etree.HTML(resp.text)
|
||||
# 通过XPath语法从页面中提取需要的数据
|
||||
spans = tree.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[1]/a/span[1]')
|
||||
for span in spans:
|
||||
print(span.text)
|
||||
time.sleep(random.randint(1, 3))
|
||||
```
|
||||
|
||||
### CSS选择器解析
|
||||
|
||||
对于熟悉CSS选择器和JavaScript的开发者来说,通过CSS选择器获取页面元素可能是更为简单的选择,因为浏览器中运行的JavaScript本身就可以`document`对象的`querySelector()`和`querySelectorAll()`方法基于CSS选择器获取页面元素。在Python中,我们可以利用三方库`bs4`(BeautifulSoup)或`pyquery`来做同样的事情。BeautifulSoup可以用来解析HTML和XML文档,修复含有未闭合标签等错误的文档,通过为待解析的页面在内存中创建一棵树结构,实现对从页面中提取数据操作的封装。可以用下面的命令来安装BeautifulSoup。
|
||||
|
||||
```Python
|
||||
pip install beautifulsoup4
|
||||
```
|
||||
|
||||
下面是使用`bs4`改写的获取豆瓣电影Top250电影名称的代码。
|
||||
|
||||
```Python
|
||||
import random
|
||||
import time
|
||||
|
||||
import bs4
|
||||
import requests
|
||||
|
||||
for page in range(1, 11):
|
||||
resp = requests.get(
|
||||
url=f'https://movie.douban.com/top250?start={(page - 1) * 25}',
|
||||
headers={
|
||||
'User-Agent': 'BaiduSpider',
|
||||
}
|
||||
)
|
||||
soup = bs4.BeautifulSoup(resp.text, 'lxml')
|
||||
# 通过CSS选择器从页面中提取需要的数据
|
||||
spans = soup.select('div.info > div.hd > a > span:nth-child(1)')
|
||||
for span in spans:
|
||||
print(span.text)
|
||||
time.sleep(random.randint(1, 3))
|
||||
```
|
||||
|
||||
关于BeautifulSoup更多的知识,可以参考它的[官方网站](https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/)。
|
||||
|
||||
### 简单的总结
|
||||
|
||||
下面我们对三种解析方式做一个简单比较。
|
||||
|
||||
| 解析方式 | 对应的模块 | 速度 | 使用难度 |
|
||||
| -------------- | ---------------- | ------ | -------- |
|
||||
| 正则表达式解析 | `re` | 快 | 困难 |
|
||||
| XPath解析 | `lxml` | 快 | 一般 |
|
||||
| CSS选择器解析 | `bs4`或`pyquery` | 不确定 | 简单 |
|
||||
|
||||
> **温馨提示**:学习中如果遇到困难,可以加**QQ交流群**询问。
|
||||
>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
## 第032课:Python中的并发编程(上)
|
||||
|
||||
####
|
||||
### 线程和进程
|
||||
|
||||
|
||||
|
||||
### 简单的总结
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
## 第033课:Python中的并发编程(下)
|
||||
|
||||
####
|
||||
### 异步编程
|
||||
|
||||
|
||||
|
||||
### 简单的总结
|
||||
|
||||
|
|
|
@ -1,6 +1,30 @@
|
|||
## 第034课:用Python访问MySQL数据库
|
||||
|
||||
####
|
||||
如果要在程序中实现数据持久化,一个特别常见的方案就是接入关系型数据库。我们之前提到过持久化这个概念,简单的说就是将内存中的数据转移到硬盘上以便长久的保存数据。接下来我们以MySQL数据库为例,来讲解在Python程序中如何使用MySQL实现数据持久化,我们这里不会介绍MySQL和关系型数据库的知识,如果没有对应的背景知识,大家可以跳过这一课,或者移步到我的Python-100-Days项目中[《关系型数据库MySQL》](https://github.com/jackfrued/Python-100-Days/blob/master/Day36-40/36-38.%E5%85%B3%E7%B3%BB%E5%9E%8B%E6%95%B0%E6%8D%AE%E5%BA%93MySQL.md)一文来了解关于关系型数据库和MySQL的知识。如果想了解在Windows或macOS环境下如何安装MySQL数据库,可以点击[传送门1](https://cloud.tencent.com/developer/article/1636375)或[传送门2](https://juejin.im/post/6844903831298375693)查看对应的文章。
|
||||
|
||||
### 准备工作
|
||||
|
||||
1. 创建名为`hrs`的数据库。
|
||||
|
||||
```SQL
|
||||
|
||||
```
|
||||
|
||||
2. 创建部门表(`tb_dept`)和员工表(`tb_emp`)。
|
||||
|
||||
```SQL
|
||||
|
||||
```
|
||||
|
||||
3. 插入测试数据。
|
||||
|
||||
```SQL
|
||||
|
||||
```
|
||||
|
||||
### Python程序接入
|
||||
|
||||
|
||||
|
||||
### 简单的总结
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
## 第035课:用Python绘制统计图表
|
||||
|
||||
####
|
||||
|
||||
|
||||
### 简单的总结
|
||||
|
||||
|
|