beautifulsoup&pyquery

beautifulsoup
和 lxml 一樣,Beautiful Soup 也是一個HTML/XML的解析器,主要的功能也是如何解析和提取 HTML/XML 數(shù)據(jù)。
lxml 只會局部遍歷,而Beautiful Soup 是基于HTML DOM的,會載入整個文檔,解析整個DOM樹,因此時間和內(nèi)存開銷都會大很多,所以性能要低于lxml。
BeautifulSoup 用來解析 HTML 比較簡單,API非常人性化,支持CSS選擇器、Python標準庫中的HTML解析器,也支持 lxml 的 XML解析器。
簡單說 : beasoup的作用是從HTML中提取數(shù)據(jù),會載入整個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)建下一頁的方式并不好,如果有下一頁可以提取該標簽的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自帶的一個解析器模塊
'''
html_bs = BeautifulSoup(html,features='lxml')
#找到職位列表
# html_bs.find() : 查找一個
# html_bs.find_all() : 查找所以符合條件的節(jié)點
'''
name = None : 指定要查找的標簽名,可以是一個字符串,正則表達式,或列表
attrs = {} : 根據(jù)屬性的值查找標簽(dict){‘屬性名稱’:‘屬性的值’}
text = None : 可以是一個字符串,正則表達式,查找符合條件的文本內(nèi)容
limit = None : 限制返回的標簽的個數(shù)
find_all : 返回的標簽都放在列表里
'''
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標簽
content_li = html_bs.select('ul.squareli li')
content = []
# 取出li標簽的文本,放入列表中
for li in content_li:
li_text = li.get_text('')
content.append(li_text)
return','.join(content)

def save_data_to_db(jobinfo):

'''

存儲數(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'標題',

type varchar(200) comment'時間',

num varchar(200) comment'人數(shù)',

address varchar(200) comment'活動介紹',

time varchar(200) comment'溫馨提示',

content varchar(200) comment'體驗店介紹'

);

Beautiful Soup將復(fù)雜HTML文檔轉(zhuǎn)換成一個復(fù)雜的樹形結(jié)構(gòu),每個節(jié)點都是Python對象,所有對象可以歸納為4種:

Tag

Tag 通俗點講就是 HTML 中的一個個標簽,例如:
<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 標簽加上里面包括的內(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 加標簽名輕松地獲取這些標簽的內(nèi)容,這些對象的類型是bs4.element.Tag。但是注意,它查找的是在所有內(nèi)容中的第一個符合要求的標簽。如果要查詢所有的標簽,后面會進行介紹。
對于 Tag,它有兩個重要的屬性,是 name 和 attrs
print(soup.name)

[document] #soup 對象本身比較特殊,它的 name 即為 [document]

print (soup.head.name)

head #對于其他內(nèi)部標簽,輸出的值便為標簽本身的名稱

print (soup.p.attrs)

{'class': ['title'], 'name': 'dromouse'}

在這里,我們把 p 標簽的所有屬性打印輸出了出來,得到的類型是一個字典。

print (soup.p['class'] # soup.p.get('class'))

['title'] #還可以利用get方法,傳入屬性的名稱,二者是等價的

soup.p['class'] = "newClass"
print soup.p # 可以對這些屬性和內(nèi)容等等進行修改

<p class="newClass" name="dromouse"><b>The Dormouse's story</b></p>

NavigableString

既然我們已經(jīng)得到了標簽的內(nèi)容,那么問題來了,我們要想獲取標簽內(nèi)部的文字怎么辦呢?很簡單,用 .string 即可,例如
print (soup.p.string)

The Dormouse's story

print (type(soup.p.string))

In [13]: <class 'bs4.element.NavigableString'>

BeautifulSoup BeautifulSoup 對象表示的是一個文檔的內(nèi)容。大部分時候,可以把它當作 Tag 對象,是一個特殊的 Tag,我們可以分別獲取它的類型,名稱,以及屬性來感受一下
print type(soup.name)

<type 'unicode'>

print soup.name

[document]

print soup.attrs # 文檔本身的屬性為空

{}

Comment Comment 對象是一個特殊類型的 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文本進行解析
安裝 : pip3 install pyquery

pq = PyQuery(html文檔)
pq(‘css選擇器’)
items():獲取到多個標簽時,使用items()將PyQuery轉(zhuǎn)換為一個生成器,然后再使用for in 循環(huán)
filter(‘css選擇器’):過濾
text():獲取標簽的文本
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):通過選擇器來獲取目標內(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>"
)

獲取第二個 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 元素,輸出第一個 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> 標簽的屬性 id
print(d('p').attr('id'))
修改 <a> 標簽的 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():
獲取下一個元素
.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:
"""
#實例化一個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 = {}
    #獲取標題(使用.text()取出文本)
    jobinfo['title'] = tr('td.l.square a').text()
    #取詳情地址,a標簽的href屬性(.attr('屬性名'))
    detail_url = 'https://hr.tencent.com/'+tr('td.l.square a').attr('href')
    #職位類型eq(1):獲取之地那個索引的標簽,索引值從0開始
    jobinfo['type'] = tr('td').eq(1).text()
    #招聘人數(shù)
    jobinfo['needpeople'] = tr('td').eq(2).text()
    #地點
    jobinfo['adress'] = tr('td').eq(3).text()
    #發(fā)布時間
    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:
"""
#實例化一個pyquery對象
html_pq = PyQuery(html)
#提取詳情內(nèi)容所在的li標簽
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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容