beautifulsoup&pyquery

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)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末婉商,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子渣叛,更是在濱河造成了極大的恐慌丈秩,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件淳衙,死亡現(xiàn)場離奇詭異癣籽,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)滤祖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瓶籽,“玉大人匠童,你說我怎么就攤上這事∷芩常” “怎么了汤求?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長严拒。 經(jīng)常有香客問我扬绪,道長,這世上最難降的妖魔是什么裤唠? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任挤牛,我火速辦了婚禮,結(jié)果婚禮上种蘸,老公的妹妹穿的比我還像新娘墓赴。我一直安慰自己,他們只是感情好航瞭,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布诫硕。 她就那樣靜靜地躺著,像睡著了一般刊侯。 火紅的嫁衣襯著肌膚如雪章办。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天滨彻,我揣著相機(jī)與錄音藕届,去河邊找鬼。 笑死疮绷,一個(gè)胖子當(dāng)著我的面吹牛翰舌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播冬骚,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼椅贱,長吁一口氣:“原來是場噩夢啊……” “哼懂算!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起庇麦,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤计技,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后山橄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體垮媒,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年航棱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了睡雇。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,566評論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡饮醇,死狀恐怖它抱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情朴艰,我是刑警寧澤观蓄,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站祠墅,受9級特大地震影響侮穿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜毁嗦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一亲茅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧金矛,春花似錦芯急、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至饼酿,卻和暖如春榕酒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背故俐。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工想鹰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人药版。 一個(gè)月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓辑舷,卻偏偏與公主長得像,于是被迫代替她去往敵國和親槽片。 傳聞我的和親對象是個(gè)殘疾皇子何缓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評論 2 348

推薦閱讀更多精彩內(nèi)容