beautifulsoup
和 lxml 一樣大咱,Beautiful Soup 也是一個(gè)HTML/XML的解析器贡避,主要的功能也是如何解析和提取 HTML/XML 數(shù)據(jù)庵寞。
lxml 只會(huì)局部遍歷北苟,而Beautiful Soup 是基于HTML DOM的奏夫,會(huì)載入整個(gè)文檔怕篷,解析整個(gè)DOM樹,因此時(shí)間和內(nèi)存開銷都會(huì)大很多酗昼,所以性能要低于lxml廊谓。
BeautifulSoup 用來解析 HTML 比較簡單,API非常人性化麻削,支持CSS選擇器蒸痹、Python標(biāo)準(zhǔn)庫中的HTML解析器,也支持 lxml 的 XML解析器呛哟。
簡單說 : beasoup的作用是從HTML中提取數(shù)據(jù)叠荠,會(huì)載入整個(gè)HTML,DOM 比lxml解析器效率低
安裝: pip3 install beautifulsoup4
eg : 以騰訊招聘為例
'''
https://hr.tencent.com/position.php?
https://hr.tencent.com/position.php?&start=10
'''
from bs4 import BeautifulSoup
import requests
import mysql.connector as c
def tengxunJob(url):
# 構(gòu)建下一頁偏移量
# next_offset = offset + 10
# 繼續(xù)發(fā)起請求竖共,解析數(shù)據(jù)
# tengxunJob(next_offset)
# 這種根據(jù)偏移量構(gòu)建下一頁的方式并不好蝙叛,如果有下一頁可以提取該標(biāo)簽的href屬性
html = load_data(full_url)
next_url = parse_page_data(html)
if 'javascript:;' != next_url:
next_url = 'https://hr.tencent.com/' + next_url
tengxunJob(next_url)
def load_data(url):
'''
發(fā)起請求,獲取職位列表頁HTML
:param url:
:return:
'''
req_header = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 '
}
response = requests.get(url,headers=req_header)
if response.status_code == 200:
return response.text
def parse_page_data(html):
'''
解析分頁的HTML源碼數(shù)據(jù)
:return:
'''
'''
features = None : 指明bs解析器
lxml : 使用lxml下的HTML解析器
html.parser : 是Python自帶的一個(gè)解析器模塊
'''
html_bs = BeautifulSoup(html,features='lxml')
#找到職位列表
# html_bs.find() : 查找一個(gè)
# html_bs.find_all() : 查找所以符合條件的節(jié)點(diǎn)
'''
name = None : 指定要查找的標(biāo)簽名公给,可以是一個(gè)字符串借帘,正則表達(dá)式,或列表
attrs = {} : 根據(jù)屬性的值查找標(biāo)簽(dict){‘屬性名稱’:‘屬性的值’}
text = None : 可以是一個(gè)字符串淌铐,正則表達(dá)式肺然,查找符合條件的文本內(nèi)容
limit = None : 限制返回的標(biāo)簽的個(gè)數(shù)
find_all : 返回的標(biāo)簽都放在列表里
'''
tr_even = html_bs.find_all(name='tr',attrs={'class':'even'})
tr_odd = html_bs.find_all(name='tr',attrs={'class':'odd'})
for tr in tr_even + tr_odd:
# print(tr)
jobinfo = {}
# get_text() : 表示取文本
jobinfo['title'] = tr.select('td.l.square > a')[0].get_text()
# 職位的類型
jobinfo['type'] = tr.select('td')[1].get_text()
# jobinfo['type'] = tr.select('td:nth-child(2)').get_text()
#
jobinfo['num'] = tr.select('td')[2].get_text()
jobinfo['address'] = tr.select('td')[3].get_text()
jobinfo['time'] = tr.select('td')[4].get_text()
# 職位詳情地址
detail_url = 'https://hr.tencent.com/' + tr.select('td.l.square > a')[0].attrs['href']
# print(detail_url)
# 詳情的HTML
html = load_data(detail_url)
# 獲取職位要求和描述
jobinfo['content'] = parse_detail_data(html)
print(jobinfo,detail_url)
# 提取url鏈接
next_url = html_bs.select('a#next')[0].attrs['href']
return next_url
# save_data_to_db(jobinfo)
def parse_detail_data(html):
# 創(chuàng)建BeautifulSoup對象
html_bs = BeautifulSoup(html,features='lxml')
#使用css語法取出li標(biāo)簽
content_li = html_bs.select('ul.squareli li')
content = []
# 取出li標(biāo)簽的文本,放入列表中
for li in content_li:
li_text = li.get_text('')
content.append(li_text)
return','.join(content)
def save_data_to_db(jobinfo):
'''
存儲(chǔ)數(shù)據(jù)
:param jobdata: 字典腿准,存放職位信息
:return:
'''
sql = '''
insert into lagou(%s)
values(%s)'''%(','.join(jobinfo.keys()),
','.join(['%s']*len(jobinfo))
)
try:
cursor.execute(sql, list(jobinfo.values()))
db.commit()
except Exception as err:
print(err)
db.rollback()
if name == 'main':
db = c.Connect(user="root", password="123456", database="1712B")
cursor = db.cursor()
# 設(shè)置起始偏移量
offset = 0
full_url = 'https://hr.tencent.com/position.php?&start=' + str(offset)
tengxunJob(full_url)
create table tengxunzp(
id int primary key auto_increment not null comment'id',
title varchar(200) comment'標(biāo)題',
type varchar(200) comment'時(shí)間',
num varchar(200) comment'人數(shù)',
address varchar(200) comment'活動(dòng)介紹',
time varchar(200) comment'溫馨提示',
content varchar(200) comment'體驗(yàn)店介紹'
);
Beautiful Soup將復(fù)雜HTML文檔轉(zhuǎn)換成一個(gè)復(fù)雜的樹形結(jié)構(gòu),每個(gè)節(jié)點(diǎn)都是Python對象,所有對象可以歸納為4種:
Tag
Tag 通俗點(diǎn)講就是 HTML 中的一個(gè)個(gè)標(biāo)簽际起,例如:
<head><title>The Dormouse's story</title></head>
<a class="sister" id="link1"></a>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
上面的 title head a p等等 HTML 標(biāo)簽加上里面包括的內(nèi)容就是 Tag拾碌,那么試著使用 Beautiful Soup 來獲取 Tags:
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 class="sister" id="link1"></a>,
<a class="sister" id="link2">Lacie</a> and
<a class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""
創(chuàng)建 Beautiful Soup 對象
soup = BeautifulSoup(html)
print(soup.title)
<title>The Dormouse's story</title>
print(soup.head)
<head><title>The Dormouse's story</title></head>
print(soup.a)
<a class="sister" id="link1"></a>
print(soup.p)
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
print(type(soup.p))
<class 'bs4.element.Tag'>
我們可以利用 soup 加標(biāo)簽名輕松地獲取這些標(biāo)簽的內(nèi)容,這些對象的類型是bs4.element.Tag街望。但是注意校翔,它查找的是在所有內(nèi)容中的第一個(gè)符合要求的標(biāo)簽。如果要查詢所有的標(biāo)簽灾前,后面會(huì)進(jìn)行介紹防症。
對于 Tag,它有兩個(gè)重要的屬性哎甲,是 name 和 attrs
print(soup.name)
[document] #soup 對象本身比較特殊蔫敲,它的 name 即為 [document]
print (soup.head.name)
head #對于其他內(nèi)部標(biāo)簽,輸出的值便為標(biāo)簽本身的名稱
print (soup.p.attrs)
{'class': ['title'], 'name': 'dromouse'}
在這里炭玫,我們把 p 標(biāo)簽的所有屬性打印輸出了出來奈嘿,得到的類型是一個(gè)字典。
print (soup.p['class'] # soup.p.get('class'))
['title'] #還可以利用get方法吞加,傳入屬性的名稱裙犹,二者是等價(jià)的
soup.p['class'] = "newClass"
print soup.p # 可以對這些屬性和內(nèi)容等等進(jìn)行修改
<p class="newClass" name="dromouse"><b>The Dormouse's story</b></p>
NavigableString
既然我們已經(jīng)得到了標(biāo)簽的內(nèi)容,那么問題來了榴鼎,我們要想獲取標(biāo)簽內(nèi)部的文字怎么辦呢伯诬?很簡單,用 .string 即可巫财,例如
print (soup.p.string)
The Dormouse's story
print (type(soup.p.string))
In [13]: <class 'bs4.element.NavigableString'>
BeautifulSoup BeautifulSoup 對象表示的是一個(gè)文檔的內(nèi)容盗似。大部分時(shí)候,可以把它當(dāng)作 Tag 對象,是一個(gè)特殊的 Tag平项,我們可以分別獲取它的類型赫舒,名稱,以及屬性來感受一下
print type(soup.name)
<type 'unicode'>
print soup.name
[document]
print soup.attrs # 文檔本身的屬性為空
{}
Comment Comment 對象是一個(gè)特殊類型的 NavigableString 對象闽瓢,其輸出的內(nèi)容不包括注釋符號接癌。
print soup.a
<a class="sister" id="link1"></a>
print soup.a.string
Elsie
print type(soup.a.string)
<class 'bs4.element.Comment'>
pyquery
pyquery
pyquery語法規(guī)則類似于Jquery,可以對html文本進(jìn)行解析
安裝 : pip3 install pyquery
pq = PyQuery(html文檔)
pq(‘css選擇器’)
items():獲取到多個(gè)標(biāo)簽時(shí),使用items()將PyQuery轉(zhuǎn)換為一個(gè)生成器扣讼,然后再使用for in 循環(huán)
filter(‘css選擇器’):過濾
text():獲取標(biāo)簽的文本
attr(‘屬性名’)獲取屬性值
.html()和.text() 獲取相應(yīng)的 HTML 塊或者文本內(nèi)容缺猛,
p=pq("<head><title>Hello World!</title></head>")
獲取相應(yīng)的 HTML 塊
print (p('head').html())
獲取相應(yīng)的文本內(nèi)容
print (p('head').text())
輸出:
<title>hello Word</title>
Hello World!
(selector):通過選擇器來獲取目標(biāo)內(nèi)容,
d = pq(
"<div><p id='item-0'>test 1</p><p class='item-1'>test 2</p></div>")
獲取 <div> 元素內(nèi)的 HTML 塊
print (d('div').html())
獲取 id 為 item-0 的元素內(nèi)的文本內(nèi)容
print (d('#item-0').text())
獲取 class 為 item-1 的元素的文本內(nèi)容
print (d('.item-1').text())
輸出:
<p id="item-0">test 1</p><p class="item-1">test 2</p>
test 1
test 2
.eq(index):根據(jù)索引號獲取指定元素(index 從 0 開始)
d = pq(
"<div><p id='item-0'>test 1</p><p class='item-1'>test 2</p></div>"
)
獲取第二個(gè) p 元素的文本內(nèi)容
print (d('p').eq(1).text())
'''輸出
test 2
'''
.find():查找嵌套元素椭符,
d = pq("<div><p id='item-0'>test 1</p><p class='item-1'>test 2</p></div>")
查找 <div> 內(nèi)的 p 元素
print d('div').find('p')
查找 <div> 內(nèi)的 p 元素荔燎,輸出第一個(gè) p 元素
print d('div').find('p').eq(0)
輸出:
<p id="item-0">test 1</p><p class="item-1">test 2</p>
<p id="item-0">test 1</p>
.filter():根據(jù) class、id 篩選指定元素销钝,
d = pq("<div><p id='item-0'>test 1</p><p class='item-1'>test 2</p></div>")
查找 class 為 item-1 的 p 元素
print d('p').filter('.item-1')
查找 id 為 item-0 的 p 元素
print d('p').filter('#item-0')
輸出:
<p class="item-1">test 2</p>
<p id="item-0">test 1</p>
.attr():獲取有咨、修改屬性值,
d = pq("<div><p id='item-0'>test 1</p><a class='item-1'>test 2</p></div>")
獲取 <p> 標(biāo)簽的屬性 id
print(d('p').attr('id'))
修改 <a> 標(biāo)簽的 class 屬性為 new
print(d('a').attr('class','new'))
輸出:
item-0
<a class="new">test 2</a>
'''
7蒸健、其他操作:
添加 class
.addClass(value):
判斷是否包含指定的 class座享,返回 True 或 False
.hasClass(value):
獲取子元素
.children():
獲取父元素
.parents():
獲取下一個(gè)元素
.next():
獲取后面全部元素塊
.nextAll():
獲取所有不匹配該選擇器的元素
.not_(selector):
eg:
from pyquery import PyQuery
import requests
def tencentJob(full_url):
html = loda_data(full_url)
next_url = parse_page_data(html)
if 'javascript:;' != next_url:
next_url = 'https://hr.tencent.com/'+next_url
tencentJob(next_url)
def loda_data(url):
"""
發(fā)起請求,獲取職位列表頁頁面源碼
:param url:
:return:
"""
req_header = {
'User-Agent':'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',
}
response = requests.get(url,headers=req_header)
if response.status_code == 200:
return response.text
def parse_page_data(html):
"""
解析分頁的頁面源碼數(shù)據(jù)
:param html:
:return:
"""
#實(shí)例化一個(gè)pyquery對象
html_pq = PyQuery(html)
#提取職位列表
# tr_even = html_pq('tr.even')
#filter過濾
tr_even = html_pq('tr').filter('.even')
tr_odd = html_pq('tr').filter('.odd')
tr_all = tr_even + tr_odd
tr_all = tr_all.items()
#print(tr_all)
# tr_even = tr_even.items()
# tr_odd = tr_odd.items()
#
# print(tr_even, tr_odd)
# print(type(tr_even), type(tr_odd))
for tr in tr_all:
# print(tr)
jobinfo = {}
#獲取標(biāo)題(使用.text()取出文本)
jobinfo['title'] = tr('td.l.square a').text()
#取詳情地址,a標(biāo)簽的href屬性(.attr('屬性名'))
detail_url = 'https://hr.tencent.com/'+tr('td.l.square a').attr('href')
#職位類型eq(1):獲取之地那個(gè)索引的標(biāo)簽,索引值從0開始
jobinfo['type'] = tr('td').eq(1).text()
#招聘人數(shù)
jobinfo['needpeople'] = tr('td').eq(2).text()
#地點(diǎn)
jobinfo['adress'] = tr('td').eq(3).text()
#發(fā)布時(shí)間
jobinfo['publishTime'] = tr('td').eq(4).text()
#工作詳情的內(nèi)容
html = loda_data(detail_url)
jobinfo['content'] = parse_detail_data(html)
print(jobinfo)
#提取下一頁的url地址
#next_url = html_pq('a').filter('#next')
next_url = html_pq('a#next').attr('href')
return next_url
def parse_detail_data(html):
"""
解析詳情數(shù)據(jù)
:param html:
:return:
"""
#實(shí)例化一個(gè)pyquery對象
html_pq = PyQuery(html)
#提取詳情內(nèi)容所在的li標(biāo)簽
lis = html_pq('ul.squareli li')
content = []
for li in lis.items():
li_text = li.text()
content.append(li_text)
return ','.join(content)
if name == 'main':
#設(shè)置起始偏移量
offset = 0
full_url = 'https://hr.tencent.com/position.php?&start=' + str(offset)
tencentJob(full_url)