python爬虫基础,正则、xpath、bs4(详细)

Python爬虫之正则表达式于xpath的使用教学及案例 *是贪婪的,会尽量多地匹配字符,直到最后一个符合条件的字符。.*?是非贪婪的,会尽量少地匹配字符,优先返回最短的符合条件的字符串。 阅读详情

爬虫步骤

爬虫一共有四个主要的步骤:

1、明确目标—知道自己需要爬哪个网站

2、爬----将目标网站内容全部爬取下来

3、取----将我们需要的数据拿出来

4、处理数据----按照我们想要的格式储存和使用

从爬开始说,请求网站,一般是get方式,post方式是传输数据的,比如将账号密码传输到服务器上,进行验证。

在爬虫最常用的请求库是,requests库,它可以请求网页,将网站返回的内容输出出来。

import requests

url = 'http://www.baidu.com'

response = requests.get(url)

requests.get(url,headers)url,headers是常用的两个参数。headers请求头中,可以添加Cookie和User-Agent,阻止反爬的手段,当然还有其他更多的方式进行反反爬,比如使用代理IP。

response.text
## 可以将网页转化为文本?,格式,以便之后的转换。

取数据有三种方法,正则表达式、xpath、BeautifulSoup4,下面一一练习。练习的网站统一使用的是职友集,这个网站相对好爬一点,没有太多反爬机制。

pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
// 将镜像源设置成国内的
pip list
// 查看pip下载的包

正则表达式

什么是正则表达式?

正则表达式,一般是用于检索,替代符合某个规则的的文本。

官方说法:正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。

使用正则表达式,我们可以1、进行匹配,查看给定的字符串是否符合正则表达式的过滤逻辑。

2、进行过滤,根据正则表达式,将文本中特定需要的内容取出来。

正则表达式的规则

语法说明表达式实例完整匹配的字符串
一般字符匹配自身abcabc
.匹配任意除了换行符\n之外的字符a.cabc
\转义字符,使用转义字符,可以将一个字符转为原来的含义。例如:\*a\.ca.c
[…]字符集,相当于可以匹配这个字符集里的任一字符。可以逐个列出,也可以写个范围,如:[abc]和[a-z]。如果第一个字符是则表示取反,例如[abc],表示不是abc的其他字符。如果要使用],-,^,可以将],-放在第一个,^不放在第一个。a[bsd]cabc、asc
\d数字:[0-9]
\D非数字:[^\d]
\s空白字符:[<空格>\t\r\n\f\v]
\S非空白字符:[^\s]
\w单词字符:[A-Za-z0-9_]
\W非单词字符:[^\w]
*匹配前一个字符0次或者无数次
+匹配前一个字符1次或者无数次
?匹配前一个字符0次或者1次
{m}匹配前一个字符m次
{m,n}匹配前一个字符m次到n次,m和n可省略一个
*?/+?/??/{m,n}?将*,+,?,{m,n}变为非贪婪模式
^匹配字符串的开头,在多行模式中(re.S)匹配每一行的开头
$匹配字符串的结尾,在多行模式中匹配每一行的结尾
\A仅匹配字符串的开头
\Z仅匹配字符串的结尾
\b匹配\w和\W之间
\B[^\b]

在这里插入图片描述

Python的re模块

在python中可以使用内置的re模块来使用正则表达式。

一般使用正则表达式有三步:1、使用compile()函数将正则表达式的字符串编译称一个Pattern对象。

2、通过Pattern对象提供的方法,对文本进行匹配查找,获得匹配结果,一个Match对象。

3、最后使用Match对象的属性和方法,获得需要的信息。

compile函数
import re

p = re.compile(r"\d+")

以上已将正则表达式进行编译成Pattern对象。

Pattern对象常用的方法有:

match方法:从起始位置开始找,一次匹配

search方法:从任意位置开始找,一次匹配

findall方法:从任意位置开始找,全部匹配,返回列表

finditer方法:从任意位置开始找,全部匹配,返回迭代器

split方法:分割字符串,返回列表

sub方法:替换字符串,返回字符串

match函数

match方法用于查找字符串的开头,也可以指定位置,它是一次匹配,匹配一次成功后,就返回。一般使用格式如下:

match(str[, pos[, endpos]])

其中str是待匹配的字符串,pos是起始位置,endpos是结束位置。默认值分别为0和len字符串长度。

当匹配成功之后,会返回一个Match对象,如果没有匹配上,则返回None

import re

p = re.compile(r'\d+')

s = '123as'

m = p.match(s)
## m为一个match对象

m.group(0)
## 123

m.start(0)
## 0,起始位置

m.end(0)
## 2,匹配的字符末尾位置

m.span(0)
## (0,3),匹配字符串的起始位置和末尾位置

在上面,当匹配成功时返回一个 Match 对象,其中:

  • group([group1, …]) 方法用于获得一个或多个分组匹配的字符串,当要获得整个匹配的子串时,可直接使用 group() 或 group(0);
  • start([group]) 方法用于获取分组匹配的子串在整个字符串中的起始位置(子串第一个字符的索引),参数默认值为 0;
  • end([group]) 方法用于获取分组匹配的子串在整个字符串中的结束位置(子串最后一个字符的索引+1),参数默认值为 0;
  • span([group]) 方法返回 (start(group), end(group))。

例如:

import re
p = re.compile(r'([a-z]+) ([a-z]+)',re.I)
## re.I是忽略大小写

m = p.match('Hello World ni hao')

m.group(0)
## Hello World,返回匹配的整个字符串

m.group(1)
## Hello,返回匹配的第一个分组

m.group(2)
## World,返回匹配的第二个分组

m.groups()
## ('Hello','World')  ,相当于(group(1),group(2),...)

m.span(1)
## (0,5),第一个分组的字符的位置
search函数

search方法,也是一次匹配,只不过是从任意位置开始匹配的。一般格式如下:

search(str[, pos[, endpos]])

str是要匹配的字符串,pos和endpos分别是起始位置和末尾位置。

同样,当匹配成功后,返回一个Match对象,如果没有,则返回None

import re
p = re.compile(r'\d+')

s = 'asd123asd'

m = p.search(s)
## m为一个match对象

m.group()
## 123

m.span()
## (3,6)
findall函数

上面的match和search都是一次匹配,findall是匹配所有符合规则的字符,将多次匹配的结果,以列表的形式返回出来。

使用形式如下:

findall(str[, pos[, endpos]])

其中,string 是待匹配的字符串,pos 和 endpos 是可选参数,指定字符串的起始和终点位置,默认值分别是 0 和 len (字符串长度)。

findall 以列表形式返回全部能匹配的子串,如果没有匹配,则返回一个空列表。

例子:

import re

p = re.compile(r'\d+')
s = 'sd123fg456fg'

p.findall(s)
## ['123','456']


p1 = re.compile(r'\d+\.\d*')
s1 = "3.14, 3.1415926, 3"

p1.findall(s1)
## ['3.14','3.1415926']
finditer函数

finditer和findall相似,都是匹配所有符合的字符,只不过findall返回的是一个列表,而finditer返回的是一个迭代器(Match对象)。

import re

p = re.compile(r'\d+')
s = 'as123as456'

m = p.finditer(s)

for i in m:
    print(i) ## 打印的i(match对象)的地址
    print(i.group()) # 打印的是匹配出来的字符
split函数

split方法,是按照能够匹配的字符分割,分割后返回列表。

split(str[, maxsplit])

其中,maxsplit 用于指定最大分割次数,不指定将全部分割。

import re
p = re.compile(r'[,;\s]+')
s = 'a,b;;c  d'

p.split(s)
## ['a','b','c','d']
sub函数

sub方法用于替换符合规则的字符。

sub(repl,string[, count])

其中,repl 可以是字符串也可以是一个函数:

  • 如果 repl 是字符串,则会使用 repl 去替换字符串每一个匹配的子串,并返回替换后的字符串,另外,repl 还可以使用 id 的形式来引用分组,但不能使用编号 0;
  • 如果 repl 是函数,这个方法应当只接受一个参数(Match 对象),并返回一个字符串用于替换(返回的字符串中不能再引用分组)。
  • count 用于指定最多替换次数,不指定时全部替换。
import re
p = re.compile(r'(\w+) (\w+)') ## \w = [A-Za-z0-9_]
s = 'hello 123,world 456'

p.sub('hello k',s)
## hello k,hello k

p.sub(r'\2 \1',s)
## 123 hello,456 world

def func(m):
    return 'hi ' + m.group(2)

p.sub(func,s)
匹配中文

在某些情况下,我们想匹配文本中的汉字,有一点需要注意的是,中文的 unicode 编码范围 主要在 [u4e00-u9fa5],这里说主要是因为这个范围并不完整,比如没有包括全角(中文)标点,不过,在大部分情况下,应该是够用的。

假设现在想把字符串 title = u’你好,hello,世界’ 中的中文提取出来,可以这么做:

import re
t = '你好,hello 世界'

p = re.compile('[\u4e00-\u9fa5]+')
p.findall(t)
注意:贪婪模式与非贪婪模式
  1. 贪婪模式:在整个表达式匹配成功的前提下,尽可能多的匹配 ( * );
  2. 非贪婪模式:在整个表达式匹配成功的前提下,尽可能少的匹配 ( ? );
  3. Python里数量词默认是贪婪的。

示例一 : 源字符串:abbbc

  • 使用贪婪的数量词的正则表达式ab*,匹配结果: abbb。

    * 决定了尽可能多匹配 b,所以a后面所有的 b 都出现了。

  • 使用非贪婪的数量词的正则表达式ab*?,匹配结果: a。

    即使前面有 *,但是 ? 决定了尽可能少匹配 b,所以没有 b。

示例二 : 源字符串:aa<div>test1</div>bb<div>test2</div>cc

  • 使用贪婪的数量词的正则表达式:<div>.*</div>
  • 匹配结果:<div>test1</div>bb<div>test2</div>

这里采用的是贪婪模式。在匹配到第一个“</div>”时已经可以使整个表达式匹配成功,但是由于采用的是贪婪模式,所以仍然要向右尝试匹配,查看是否还有更长的可以成功匹配的子串。匹配到第二个“</div>”后,向右再没有可以成功匹配的子串,匹配结束,匹配结果为“<div>test1</div>bb<div>test2</div>”


  • 使用非贪婪的数量词的正则表达式:<div>.*?</div>
  • 匹配结果:<div>test1</div>

正则表达式二采用的是非贪婪模式,在匹配到第一个“</div>”时使整个表达式匹配成功,由于采用的是非贪婪模式,所以结束匹配,不再向右尝试,匹配结果为“<div>test1</div>”。

正则表达式测试网址

使用正则爬取工作信息

import requests
import re
import time

url = 'https://www.jobui.com/jobs?cityKw=%E9%83%91%E5%B7%9E&jobKw=%E6%95%B0%E6%8D%AE'
header = {'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0'
          ,'Cookie' : 'AXUDE_jobuiinfo=xPq3I7eEuU; jobui_p=1603107811866_15411384; TN_VisitCookie=95; jobui_user_passport=yk160310782641343; isloginStatus=hQDDe3yhKHw%3D; jobui_user_searchURL=http%3A%2F%2Fm.jobui.com%2Fjobs%3FjobKw%3D%25E6%2595%25B0%25E6%258D%25AE%25E5%2588%2586%25E6%259E%2590%25E5%25B8%2588%26cityKw%3D%25E5%258C%2597%25E4%25BA%25AC%26experienceType%3D1-3%25E5%25B9%25B4%26sortField%3Dlast; jobui_area=%25E9%2583%2591%25E5%25B7%259E; Hm_lvt_8b3e2b14eff57d444737b5e71d065e72=1619854217; Hm_lpvt_8b3e2b14eff57d444737b5e71d065e72=1619854229; TN_VisitNum=2; PHPSESSID=ov59buf4dfqo8bmpbgjk06s0f0'}


response = requests.get(url,headers=header)

res = response.text
p = re.compile(r'class="job-name" href="(.*?)" target="_blank"')

l = p.findall(res)

urls = 'https://www.jobui.com'

j=0

for i in l:
    url0 = urls+i
    response = requests.get(url0,headers=header)
    r0=response.text

    title = re.findall(r'<h1 title=.*?style="margin-bottom: 20px;">(.*?)</h1>',r0)
    yaoqiu = re.findall(r'<span>(.*?)</span>[a-z<\d\s="]*>?(.*?)[</span>]*</li>',r0,re.I)
    gangwei = re.findall(r'style="word-break: break-all;">\r\n\t[\s]+(.*?)\r\n\t[\s]+</div>',r0,re.S)[0].replace('<br />','')

    y=''
    for i in yaoqiu:
        y = i[0]+i[1]+'\n'

    with open('./工作.txt','a',encoding='utf-8') as f:
        f.write(title[0]+'\n')
        f.write(y+'\n')
        f.write(gangwei+'\n')
    time.sleep(1)
    print('已保存'+str(j+1)+'个')
    j+=1

xpath

如果有的数据使用正则比较麻烦,可以将HTML转换为XML类型,使用xpath语法来取出想要的数据。

什么是XML?

官方解释:

  • XML 指可扩展标记语言(EXtensible Markup Language)
  • XML 是一种标记语言,很类似 HTML
  • XML 的设计宗旨是传输数据,而非显示数据
  • XML 的标签需要我们自行定义。
  • XML 被设计为具有自我描述性。
  • XML 是 W3C 的推荐标准

XML文档示例

<?xml version="1.0" encoding="utf-8"?>

<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">XQuery Kick Start</title>  
    <author>James McGovern</author>  
    <author>Per Bothner</author>  
    <author>Kurt Cagle</author>  
    <author>James Linn</author>  
    <author>Vaidyanathan Nagarajan</author>  
    <year>2003</year>  
    <price>49.99</price> 
  </book> 

  <book category="web" cover="paperback"> 
    <title lang="en">Learning XML</title>  
    <author>Erik T. Ray</author>  
    <year>2003</year>  
    <price>39.95</price> 
  </book> 

</bookstore>

XML的节点关系

节点和节点之间有父、子、同胞、先辈、后代关系。

<?xml version="1.0" encoding="utf-8"?>

<bookstore>

<book>
  <title>Harry Potter</title>
  <author>J K. Rowling</author>
  <year>2005</year>
  <price>29.99</price>
</book>
    
</bookstore>

以上面的xml为例

book是title、author、year、price的父。

title、year是book的子

title、year是同胞

bookstore是year、price的先辈

year是bookstore的后代

XPath是什么?

xpath是一门语言,可以查询xml格式的数据,可用来遍历xml文档中的元素和属性。

XPath 开发工具

  1. 开源的XPath表达式编辑工具:XMLQuire(XML格式文件可用)
  2. Chrome插件 XPath Helper
  3. Firefox插件 XPath Checker

xpath语法–选取节点

xpath使用的路径表达式来选取xml文档中的节点或者节点集,和操作系统中的文件系统近似。

下面写出常用的路径表达式:

表达式描述
nodename选取此节点的所有子节点
/从根节点选取
//从匹配选择的当前节点选择文档的节点,不考虑节点的位置
.选取当前节点
…选取当前节点的父节点
@选取属性

例子:

路径表达式结果
bookstore选取 bookstore 元素的所有子节点。
/bookstore选取根元素 bookstore。注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径!
bookstore/book选取属于 bookstore 的子元素的所有 book 元素。
//book选取所有 book 子元素,而不管它们在文档中的位置。
bookstore//book选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。
//@lang选取名为 lang 的所有属性。

xpath语法–谓语

谓语是查找某个特定的节点或者包含指定的值的节点,写在方括号[]中。

例子:

路径表达式结果
/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’]选取拥有并且lang属性为eng的title元素
/bookstore/book[price>35.00]选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。
/bookstore/book[price>35.00]/title选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。

xpath语法–选取未知节点

XPath 通配符可用来选取未知的 XML 元素。

通配符描述
*匹配任何元素节点。
@*匹配任何属性节点。
node()匹配任何类型的节点。

在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:

路径表达式结果
/bookstore/*选取 bookstore 元素的所有子元素。
//*选取文档中的所有元素。
//title[@*]选取所有带有属性的 title 元素。

xpath语法–选取若干路径

通过在路径表达式中使用“|”运算符,您可以选取若干个路径。

在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:

路径表达式结果
//book/title | //book/price选取 book 元素的所有 title 和 price 元素。
//title | //price选取文档中的所有 title 和 price 元素。
/bookstore/book/title | //price选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素。

XPath的运算符

下面列出了可用在 XPath 表达式中的运算符:

在这里插入图片描述

这些就是XPath的语法内容,在运用到Python抓取时要先转换为xml。

lxml库

官方解释:

lxml 是 一个HTML/XML的解析器,主要的功能是如何解析和提取 HTML/XML 数据。

lxml和正则一样,也是用 C 实现的,是一款高性能的 Python HTML/XML 解析器,我们可以利用之前学习的XPath语法,来快速的定位特定元素以及节点信息。

lxml python 官方文档:http://lxml.de/index.html

需要安装C语言库,可使用 pip 安装:pip install lxml

我们可以使用它来解析HTML文档。

from lxml import etree

text = '''
<div>
    <ul>
         <li 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> # 注意,此处缺少一个 </li> 闭合标签
     </ul>
 </div>
'''

html = etree.HTML(text)

print(etree.tostring(html))

lxml可以修正html代码,例子中它自动补全了li标签,还添加了boby,html标签。

xpath实例

新建一个文件hello.html,内容如下:

<!-- hello.html -->

<div>
    <ul>
         <li 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"><span class="bold">third item</span></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></li>
     </ul>
 </div>

使用etree.parse('hello.html')来读取数据。

from lxml import etree

html = etree.parse('hello.html')

r0 = html.xpath('//li')
# 获取所有的li标签元素

r1 = html.xpath('//li/@class')
# 获取所有li标签的calss属性值
# ['item-0', 'item-1', 'item-inactive', 'item-1', 'item-0']

r2 = html.xpath('//li/a[@href="link4.html"]/text()')
# 获取所有li标签有下a标签,且a标签的href属性为link4.html,的a标签的值

r3 = html.xpath('//li//span')
# 获取li标签下的span标签,注意span是li的后代,不是子元素,所以需要用双斜杠//

r4 = html.xpath('//li/a//@class')
# 获取li标签下的a标签的所有class属性值
# ['bold']

r5 = html.xpath('//li[last()]/a/@href')
# 获取最后一个li标签下的a标签的href属性值

r6 = html.xpath('//li[last()-1]/a/text()')
# 获取倒数第二个li标签下的a标签的值

r7 = html.xpath('//*[@class="bold"]')
r7[0].tage
# 获取class值为bold的标签名

使用xpath爬取工作信息

import requests
from lxml import etree
import random
import time

city = input('请输入想要查询的城市:')
job = input('请输入想要查询的工作:')
t = 0
while True:
    try :
        page = input('请输入你想要的页数:')
        page = int(page)
    except ValueError as e:
        print('你输入的页数不规范,请重新输入。\n'+str(e)+'\n')
    else :
        print('输入规范!')
        break


url2 = 'https://www.jobui.com'
headers = {'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0'
          ,'Cookie' : 'AXUDE_jobuiinfo=xPq3I7eEuU; jobui_p=1603107811866_15411384; TN_VisitCookie=95; jobui_user_passport=yk160310782641343; isloginStatus=hQDDe3yhKHw%3D; jobui_user_searchURL=http%3A%2F%2Fm.jobui.com%2Fjobs%3FjobKw%3D%25E6%2595%25B0%25E6%258D%25AE%25E5%2588%2586%25E6%259E%2590%25E5%25B8%2588%26cityKw%3D%25E5%258C%2597%25E4%25BA%25AC%26experienceType%3D1-3%25E5%25B9%25B4%26sortField%3Dlast; jobui_area=%25E9%2583%2591%25E5%25B7%259E; Hm_lvt_8b3e2b14eff57d444737b5e71d065e72=1619854217; Hm_lpvt_8b3e2b14eff57d444737b5e71d065e72=1619854229; TN_VisitNum=2; PHPSESSID=ov59buf4dfqo8bmpbgjk06s0f0'}


for n in range(page):
    url = "https://www.jobui.com/jobs?jobKw="+job+"&cityKw="+city+"&n="+str(n+1)
    res = requests.get(url,headers=headers)

    html = etree.HTML(res.text)
    a = html.xpath('//a[@class="job-name"]/@href')

    for i in a:
        url0=url2+i
        re = requests.get(url0,headers=headers)
        ht = etree.HTML(re.text)

        title = ht.xpath('//h1[@style="margin-bottom: 20px;"]/text()')
        if title==[]:
            continue
        else:
            title = title[0]
            t += 1

        yao = ht.xpath('//ul[@class="laver cfix fs16"]//li//text()')
        b = dict(zip(yao[::2], yao[1::2]))
        gw= ht.xpath('//div[@class="hasVist cfix sbox fs16"]//text()')

        with open(city+job+'.txt','a',encoding='utf-8') as f:
            f.write(str(title) + '\n')
            for k,v in b.items():
                f.write(k + v + '\n')

            for j in gw:
                f.write(j.strip()+'\n')
            f.write('\n\n')

        print('保存完成'+str(t)+'个')
    time.sleep(random.random()*3)

print('爬取完成')

BeautifulSoup4

和 lxml 一样,Beautiful Soup 也是一个HTML/XML的解析器,主要的功能也是如何解析和提取 HTML/XML 数据。

lxml 只会局部遍历,而Beautiful Soup 是基于HTML DOM的,会载入整个文档,解析整个DOM树,因此时间和内存开销都会大很多,所以性能要低于lxml。

BeautifulSoup 用来解析 HTML 比较简单,API非常人性化,支持CSS选择器、Python标准库中的HTML解析器,也支持 lxml 的 XML解析器。

Beautiful Soup 3 目前已经停止开发,推荐现在的项目使用Beautiful Soup 4。使用 pip 安装即可:pip install beautifulsoup4

官方文档:http://beautifulsoup.readthedocs.io/zh_CN/v4.4.0

抓取工具速度使用难度安装难度
正则最快困难无(内置)
BeautifulSoup慢最简单简单
lxml快简单一般

例子

from bs4 import BeautifulSoup

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>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""


soup = BeautifulSoup(html)

soup.prettify()
# 格式化输出soup中的内容

四大对象种类

Beautiful Soup将复杂的HTML文档转换成一个复杂的树形结构,每一个节点都是一个python对象。所有对象可以总结成四种:

  • Tag
  • NavigableString
  • BeaytifulSoup
  • Comment
Tag

Tag就是html中的一个个标签。例如

<head><title>The Dormouse's story</title></head>
<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>

上面的head、title、a、p都是一个Tag

from bs4 import BeautifulSoup

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>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""

soup = BeautifulSoup(html)

soup.title
# <title>The Dormouse's story</title>

soup.head
# <head><title>The Dormouse's story</title></head>

type(soup.title)
# bs4.element.Tag

soup.a
# <a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>

我们可以利用soup加标签名,轻松的获取标签的内容,这些对象的类型是bs4.element.Tag,但是它只是查找第一个符合条件的标签。若需要找到所有符合的标签,可以使用find_all或select。

对于Tag它有两个重要的属性,name和attrs

soup.name
# soup 对象本身比较特殊,它的 name 即为 [document]

soup.title.name
# title,此节点的名字

soup.p.attrs
# 第一个p节点的属性及属性值
# {'class': ['title'], 'name': 'dromouse'}

soup.p['class']
# p节点的class属性值
# ['title']

soup.p['class'] = "newClass"
soup.p['class']
# 修改p节点的class的属性值,为newClass

del soup.p['class']
soup.p
# 删除p节点的class属性
NavigableString

若需要标签的值(标签内部的文字),可直接加上.string即可

soup.p.string
# "The Dormouse's story"

type(soup.p.string)
# bs4.element.NavigableString
BeautifulSoup

BeautifulSoup对象表示一个文档的内容,可以把它看作一个特殊的Tag。

type(soup.name)
# str

soup.name
# '[document]'

soup.attrs
# {}
Comment

Comment对象是一个特殊的NavigableString对象,其输出的内容不包括注释符号。

soup.a
# <a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>

soup.a.string
# ' Elsie '

type(soup.a.string)
# bs4.element.Comment

遍历文档树

直接子节点,contents和children

.contents

tag的contents属性可以将tag的子节点以列表的方式输出

soup.head.contents
# [<title>The Dormouse's story</title>]

.children

它不是返回的列表,而是一个迭代器。

soup.head.children
# <list_iterator at 0x114c2c8>

for i in soup.body.children:
    print(i)
# <p 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 class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
# and they lived at the bottom of a well.</p>
# <p class="story">...</p>
所有的子孙节点,descendants

.contents 和 .children 属性仅包含tag的直接子节点,.descendants 属性可以对所有tag的子孙节点进行递归循环,和 children类似,我们也需要遍历获取其中的内容。

for i in soup.descendants:
    print(i)
节点内容,string

如果tag只有一个 NavigableString 类型子节点,那么这个tag可以使用 .string 得到子节点。如果一个tag仅有一个子节点,那么这个tag也可以使用 .string 方法,输出结果与当前唯一子节点的 .string 结果相同。

简单点说,如果一个标签中没有子标签,它就会返回标签中的内容。如果标签里面只有唯一的一个标签了,那么 .string 也会返回最里面的内容。

soup.head.string
# "The Dormouse's story"

soup.title.string
# "The Dormouse's story"

搜索文档树

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

返回一个符合条件的列表。

name参数

name参数可以查找所有名字为name的tag,字符串对象会被自动忽略掉。

传字符串

最简单的就是传入一个字符串,它会寻找与字符串相同的所有tag。

soup.find_all('b')
# [<b>The Dormouse's story</b>]

soup.find_all('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>]

传正则表达式

若传入正则表达式作为参数,Beautiful Soup会通过正则表达式的match()来匹配内容。

import re

for i in soup.find_all(re.compile(r'^b')):
    print(i.name)

# body
# b

传列表

如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回.

soup.find_all(['a','b'])
# [<b>The Dormouse's story</b>,
# <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>]
keyword参数

可以根据节点的属性以及属性值筛选

soup.find_all(id='link2')
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
text参数

可以通过text参数寻找文档的内容,与name参数一样,可以传入字符串、正则(search)、列表。

soup.find_all(text=' Elsie ')
# [' Elsie ']

soup.find_all(text=["Tillie", " Elsie ", "Lacie"])
# [' Elsie ', 'Lacie', 'Tillie']

soup.find_all(text=re.compile("Dormouse"))
[u"The Dormouse's story", u"The Dormouse's story"]
CSS选择器

CSS选择器和find_all相似。

  • 写 CSS 时,标签名不加任何修饰,类名前加.,id名前加#
  • 在这里我们也可以利用类似的方法来筛选元素,用到的方法是 soup.select(),返回类型是 list
通过标签名找
soup.select('title')
# [<title>The Dormouse's story</title>]

soup.select('b')
#[<b>The Dormouse's story</b>]
通过类名找
soup.select('.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>]
通过id找
soup.select('#link3')
# [<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
组合查找

组合查找即和写 class 文件时,标签名与类名、id名进行的组合原理是一样的,例如查找 p 标签中,id 等于 link1的内容,二者需要用空格分开

soup.select('p #link1')
# [<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>]

子标签查询,使用>分隔,或者空格分隔

soup.select('head > title')
# [<title>The Dormouse's story</title>]

soup.select('head title')
# [<title>The Dormouse's story</title>]
属性查找

查找时还可以加入属性元素,属性需要用中括号括起来,注意属性和标签属于同一节点,所以中间不能加空格,否则会无法匹配到。类似于xpath的属性语法。

soup.select('a[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>]

属性仍然可以与上述查找方式组合,不在同一节点的空格隔开,同一节点的不加空格

soup.select('p a[href="http://example.com/elsie"]')
# [<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>]
获取内容

select方法返回的都是Tag列表,可以使用get_text()获取内容

type(soup.select('title')[0])
# bs4.element.Tag

soup.select('a[id="link2"]')[0].get_text()
# 'Lacie'

使用BeautifulSoup4爬取工作信息

from bs4 import BeautifulSoup
import requests
import time

header = {'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0'
          ,'Cookie' : 'AXUDE_jobuiinfo=xPq3I7eEuU; jobui_p=1603107811866_15411384; TN_VisitCookie=95; jobui_user_passport=yk160310782641343; isloginStatus=hQDDe3yhKHw%3D; jobui_user_searchURL=http%3A%2F%2Fm.jobui.com%2Fjobs%3FjobKw%3D%25E6%2595%25B0%25E6%258D%25AE%25E5%2588%2586%25E6%259E%2590%25E5%25B8%2588%26cityKw%3D%25E5%258C%2597%25E4%25BA%25AC%26experienceType%3D1-3%25E5%25B9%25B4%26sortField%3Dlast; jobui_area=%25E9%2583%2591%25E5%25B7%259E; Hm_lvt_8b3e2b14eff57d444737b5e71d065e72=1619854217; Hm_lpvt_8b3e2b14eff57d444737b5e71d065e72=1619854229; TN_VisitNum=2; PHPSESSID=ov59buf4dfqo8bmpbgjk06s0f0'}
url = 'https://www.jobui.com/jobs?cityKw=%E9%83%91%E5%B7%9E&jobKw=%E6%95%B0%E6%8D%AE'
resp = requests.get(url,headers=header)
soup = BeautifulSoup(resp.text,'lxml')

link = soup.select('a[class="job-name"]')

k = 1

for i in link:
    url0 = 'https://www.jobui.com'+i['href']
    res0 = requests.get(url0,headers=header)
    soup0 = BeautifulSoup(res0.text, 'lxml')
    try:
        title = soup0.select('div[class="jk-box jk-matter j-job-detail"] > h1')[0].get_text()

        yaoqiu = soup0.select('ul[class="laver cfix fs16"] > li')
        yq = ''
        for j in yaoqiu:
            yq += (j.get_text()+"\n")

        zhize = soup0.select('div[class="hasVist cfix sbox fs16"]')[0].get_text().replace('\r\n\t                    ',"")
    except IndexError:
        continue

    with open('job.txt','a',encoding='utf-8') as f:
        f.write(title+"\n\n工作要求"+yq+"\n岗位职责"+zhize+'\n-----------------------\n')

    print('已完成'+str(k)+"个")
    time.sleep(1)
    k += 1

python爬虫学习系列之正则、bs4、xpath、jsonpath python爬虫学习系列之正则、bs4、xpath、jsonpath 正则 bs4 xpath jsonpath 阅读详情

相关推荐

Python爬虫数据解析(聚焦爬虫) 正则、BS4、Xpath

数据解析(聚焦爬虫) 聚焦爬虫:爬取页面中指定的页面内容。 聚焦爬虫的编码流程: 指定url 发起请求 获取响应数据 数据解析 持久化存储 建立在通用爬虫基础之上,先爬取整个页面,之后进行指定局部的数据进行提取。这个提取的过程就称之为数据解析 数据解析分类: 正则 bs4 Xpath(最通用的,重点) 数据解析原理: 解析的局部内容基本上会存储在标签之间或标签的属性中; 进行指定的标签定位 标签之间或标签的属性中存储的数据进行提取(解析) 爬取图片数据 content 二进制数据 text 文

qq_38232003的博客 885

网页解析工具(XPath与正则表达式)

XPath lxml库 功能 lxml是一个HTML/XML的解析器,主要的功能是如何解析和提取HTML/XML数据 基本使用 利用来解析HTML代码,并在解析时,如果HTML代码不规范,将自动进行补全 from lxml import etree text = ''' <div> hello world </div> ''' html = etree.HTML(text) #返回html对象 #html = etree.parse('hello.html') #从文件读取ht

songwei598的博客 3222

Python大数据分析&人工智能教程 - 页面解析Xpath和正则表达式(详解与案例)

在本章节中,我们深入探讨了Python在大数据分析与人工智能领域中的应用,特别是页面解析技术的关键组成部分:Xpath和正则表达式。通过对这两种技术的详细讲解和实例演示,我们展示了如何有效地从HTML文档中提取数据,并利用正则表达式进行文本处理。

专注于人工智能、软件开发、工控自动化、工厂数字化及智能化等领域,希望和大家共同进步! 2280

python爬虫之正则,xpath,bs4基础语法

正则表达式,又称规则表达式(Regular Expression,在代码中常简写为regex、regexp或RE),是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为"元字符"),是计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列匹配某个语法规则的字符串,通常被用来检索、替换那些符合某个模式(规则)的文本。—正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。—正则表达式使用单个字符串来描述、匹配一系列匹配某个语法规则的字符串。

qq_55755280的博客 1197

Python 爬虫实战:对比不同解析库的效率(BS4/XPath/ 正则)

摘要:本文对比分析Python爬虫中BS4、XPath和正则表达式三种解析库的性能差异。通过标准化测试发现,正则表达式解析速度最快(0.0083秒),是BS4的9.69倍,内存占用最低(0.92MB);XPath性能居中(0.0210秒),容错性较好;BS4(0.0804秒)虽速度最慢但容错性最佳。测试表明:正则表达式适合高性能简单文本提取,XPath适用于结构化数据解析,BS4则擅长处理不规范HTML。实际开发建议根据数据规模、HTML规范性和性能需求灵活选择,复杂场景可采用混合解析策略。本文提供了可复现

2503_91057718的博客 479

HA集群 执行 hdfs haadmin -transitionToActive 失败

最近把一个单NameNode集群变更为HA集群,过程中都很顺利,nn1和nn2两个NameNode都显示standby状态,但是把nn1或者nn2切换到active状态时就会报错抛异常。 查看master上namenode启动日志,如下: Incompatible clusterID for journal Storage Directory /home/hadoop-zero/bigdat...

qq_34727158的博客 1763

正则 Xpath语法

正则:正规表达式 用来处理字符串 单字符匹配 . 除换行之外的任意字符 \d 表示数字 \D 匹配非数字 \w 匹配单词字符[a-z,A-Z,0-9] \W 匹配非单词字符 \s 匹配空白字符,空格,\n ,\t \S 匹配非空白字符 ^ 匹配以什么开头 & 匹配以什么结尾 [0-9] 等价于\d 匹配0-9 多字符匹配(贪婪模式) 匹配*前面的字符任意次数(0-无限) 匹配+前面的字符至...

pc_cp_ccp的博客 1118

用Python实现古诗词填字游戏(三)

古诗词做填字游戏的第三部分代码,也是最核心的部分。实现对诗文和关键字的选定,并确定文字的输出位置。

PosPro的专栏 6117

Python网络爬虫之三种数据解析方式 (xpath, 正则, bs4)

引入 回顾requests实现数据爬取的流程 指定url 基于requests模块发起请求 获取响应对象中的数据 进行持久化存储 其实,在上述流程中还需要较为重要的一步,就是在持久化存储之前需要进行指定数据解析。因为大多数情况下的需求,我们都会指定去使用聚焦爬虫,也就是爬取页面中指定部分的数据值,而不是整个页面的数据。因此,本次课程中会给大家详细介绍讲解三种聚焦爬...

PresleyR的博客 506

StarUML最新版汉化与破解二合一教程:5分钟搞定永久使用

本文提供StarUML最新版的高效使用指南,涵盖从安装到深度定制的全流程优化方案。包括软件获取最佳实践、界面个性化设置、核心建模技巧、团队协作策略及性能优化建议,帮助架构师和开发团队提升UML建模效率。特别推荐版本控制集成和文档自动化输出方案,适用于各类规模的项目开发。

weixin_42530732的博客 450

CAN:CANape使用Function处理数据

CANape中的function and script习惯称之为脚本(实际为函数和脚本)。这里介绍function,可以实现简单信号a和信号b乘法,比如计算功率(转速与扭矩乘)。该功能前期有专业人员做支援,当时问其编制方法,其回答是看帮助文件。现在没有支援自己动手。 功能描述:电压信号阶梯增加,每次保持1.5s,测量后0.5s位移平均值(假设信号步长10ms,需要取样50个点,但该脚本中点的数目为10个)。图示如下: 刚开始看了软件Function definition→Library function,

Morven 1万+

【基础】【Python网络爬虫】【5.数据解析】bs4、Xpath、Parsel模块、正则表达式(附大量案例代码)(建议收藏)

数据解析 1. 为何数据解析 2. 常见的数据类型 结构化数据 半结构化数据 非结构化数据 3. 爬虫项目实现步骤 数据解析模块 1. Bs4 环境安装 bs4解析流程 案例 - bs4碧血剑文本爬取 2. Xpath 环境安装 xpath解析的编码流程 xpath表达式如何理解? 案例 - 简历模板下载 案例 - 爬取空气质量数据网 案例 - (彼岸图)图片数据爬取 3. Parsel 模块 CSS 标签选择器 类选择器 ID选择器 组合选择器 伪类选择器 案例 - css解析小说1 提取属性和文本数据

weixin_43612602的博客 2117

2019年数学建模美赛经验总结

北京时间2019年1月29号上午9时,数模美赛结束。美赛结束已经10多天了,我在这次竞赛中负责建模和编程,趁现在记忆还比较清晰,写下这篇博客记录第一次参加数学建模美赛的经历。 补更:2019年4月20日公布成绩,笔者最终获得了美赛一等奖 目录 1、基本情况 2、选题 3、分工合作 4、论文写作 5、建模 6、编程 7、入门资源 ...

Smallactive 7万+

Python网络爬虫四大选择器(正则表达式、BS4、Xpath、CSS)总结

Python网络爬虫四大选择器(正则表达式、BS4、Xpath、CSS)总结 0.前言 相关实战文章: 正则表达式、BeautifulSoup、Xpath、CSS选择器分别抓取京东网的商品信息。 网络爬虫:模拟客户端批量发送网络请求,批量接收请求对应的数据,按照一定的规则,自动抓取互联网信息的程序,进行数据采集,并进行持久化存储。其他用途:百度搜索, 12306抢票、各种抢购、投票、刷票、短信轰炸、网络攻击、Web漏洞扫描器 都是爬虫技术。 爬虫的用途 1.金融 金融新闻/数据 制定投资策

qq_35249586的博客 1315

5、癌症数据预处理技术与深度学习在癌症预测中的应用

本文详细介绍了癌症数据预处理技术和深度学习在癌症预测中的应用。针对CT扫描图像、X射线图像、超声图像、基因表达数据和文本数据等不同类型的癌症数据,分别阐述了去噪、插值、配准、归一化等预处理技术的具体方法。同时,分析了深度学习在乳腺癌、白血病和宫颈癌预测中的作用,结合相关影响因素探讨了模型的应用潜力。此外,还总结了深度学习在癌症预测中面临的挑战及未来发展方向,为癌症的早期诊断与精准预测提供了理论支持和技术参考。

alice7model的博客 123

十.镜头知识之畸变问题

对于理想光学系统,在一对共轭的物像平面上,像的放大率是一个常数。但是对于实际的光学系统,当视场较大或很大时,像的放大率就要随视场而异,这样就会使像相对于物体失去相似性。这种使像变形的成像缺陷称为畸变。

AIoT深度探索者 | 嵌入式系统架构师 深耕物联网(IoT)与人工智能(AI)融合技术,聚焦​​边缘计算、图像开发、低功耗通信协议​​,致力于智能硬件,关注我获取最新技术文章信息与海量资源~ 2120

Python爬虫 之 聚焦爬虫(数据解析 正则,bs4,xpath)

Python爬虫 之 聚焦爬虫(数据解析) 全套爬虫博文等你来访

[ 25'h ] 4264

【C2000系列DSP的Bootloader详解】如何打包源文件为库文件且加密仅授权使用,一步步教你如何实现

如果想要在 TI 的 CCS 开发环境中为 C2000 系列 MCU 生成 lib 库文件,并对这些库文件进行加密保护,确保只有授权方(持有密钥)才能调用和使用,以此保护源代码的知识产权。

昔时扬尘处 176

Python爬虫四大选择器(正则、BS4、Xpath、CSS)介绍 / 代码实例

(图源以及参考:python爬虫系列 https://www.jianshu.com/p/4424c7f36143) 爬虫的流程:请求网页 - 解析网页 - 提取网页数据 - 保存数据 (1)请求网页 (2)解析网页、提取数据的组合:bs4 + lxml 或者 lxml+xpath等 (3)保存数据 文章目录Json库lxml库requests 库正则表达式bs4xpath Json库 bs4...

Quest_sec的博客 2751

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

python爬虫xpath提取数据_Python网络爬虫四大选择器(正则表达式、BS4、Xpath、CSS)总结...

前几天小编连续写了四篇关于Python选择器的文章,分别用正则表达式、BeautifulSoup、Xpath、CSS选择器分别抓取京东网的商品信息。今天小编来给大家总结一下这四个选择器,让大家更加深刻的理解和熟悉Python选择器。 一、正则表达式 正则表达式为我们提供了抓取数据的快捷方式。虽然该正则表达式更容易适应未来变化,但又存在难以构造、可读性差的问题。当在爬京东网的时候,正则表达式如下图所...

weixin_39634438的博客 119

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

python爬虫哪个选择器好用_Python网络爬虫四大选择器(正则表达式、BS4、Xpath、CSS)总结...

前几天小编连续写了四篇关于Python选择器的文章,分别用正则表达式、BeautifulSoup、Xpath、CSS选择器分别抓取京东网的商品信息。今天小编来给大家总结一下这四个选择器,让大家更加深刻的理解和熟悉Python选择器。一、正则表达式正则表达式为我们提供了抓取数据的快捷方式。虽然该正则表达式更容易适应未来变化,但又存在难以构造、可读性差的问题。当在爬京东网的时候,正则表达式如下图所示:...

weixin_40000301的博客 116

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

上一篇: shell基本语法
下一篇: python爬虫之selenium库
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力生活的黄先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值