認識網(wǎng)絡(luò)爬蟲
網(wǎng)絡(luò)爬蟲是指一種程序自動獲取網(wǎng)頁信息的方式,它能夠自動化地獲取互聯(lián)網(wǎng)上的數(shù)據(jù)。通過使用網(wǎng)絡(luò)爬蟲,我們可以方便地獲取到網(wǎng)絡(luò)上的各種數(shù)據(jù),例如網(wǎng)頁鏈接、文本、圖片、音頻、視頻等等。
HTML頁面組成
網(wǎng)頁是由HTML標(biāo)簽和內(nèi)容組成,HTML標(biāo)簽通過標(biāo)簽屬性可以定位到需要的內(nèi)容。網(wǎng)頁中的樣式由CSS控制,JavaScript可以實現(xiàn)網(wǎng)頁動態(tài)效果。
HTML標(biāo)簽是一種用于構(gòu)建Web頁面的標(biāo)記語言,它描述了頁面的結(jié)構(gòu)和元素。HTML標(biāo)簽通常包含一個起始標(biāo)簽和一個結(jié)束標(biāo)簽,例如<div>
和</div>
。HTML標(biāo)簽也可以包含屬性,屬性用于提供有關(guān)元素的額外信息。例如,<a>
元素的href
屬性指定了鏈接目標(biāo)的URL地址,而<img>
元素的src
屬性指定了要顯示的圖像文件的URL地址。
CSS是一種用于控制Web頁面樣式的樣式表語言,它可以為HTML元素提供樣式和布局。通過CSS,我們可以控制文本的字體、顏色、大小和樣式,以及元素的大小、位置、邊框和背景等。
JavaScript是用于實現(xiàn)Web頁面動態(tài)效果的一種編程語言,它可以實現(xiàn)網(wǎng)頁上的各種交互效果,例如彈出窗口、表單驗證、動畫效果等。
Requests模塊get請求與實戰(zhàn)
Requests是Python中的HTTP庫,提供了簡潔易用的接口進行HTTP請求。其中,GET請求常用于獲取靜態(tài)網(wǎng)頁信息。
我們通過requests.get()方法來發(fā)送一個GET請求.
import requests
url = 'https://www.baidu.com'
response = requests.get(url)
print(response.text)
效果圖
代碼解析
第一行導(dǎo)入了requests模塊,第二行指定了要請求的URL地址,在本例中我們使用百度首頁作為示例。第三行使用requests庫的get()方法來獲取該URL的響應(yīng)對象。響應(yīng)對象包含了服務(wù)器返回的所有信息,包括Header(頭部)和Body(主體)兩部分。其中Header包含了很多信息,如日期、內(nèi)容類型、服務(wù)器版本等,而Body包含了頁面HTML源代碼等具體信息。
第四行使用print()函數(shù)打印出響應(yīng)內(nèi)容的文本形式。運行這段代碼,我們就可以在終端中看到百度首頁的HTML源代碼。
在實際爬蟲中,我們可以利用requests模塊的一些屬性或者方法來解析響應(yīng)內(nèi)容,提取需要的數(shù)據(jù)。例如,可以使用response.status_code屬性來獲取HTTP狀態(tài)碼,使用response.headers屬性來獲取HTTP頭部信息等。此外,我們還可以使用response.json()方法來解析JSON格式的響應(yīng)內(nèi)容,使用response.content方法來獲取字節(jié)形式的響應(yīng)內(nèi)容等。
Post請求與實戰(zhàn)
POST請求與GET請求的區(qū)別在于,POST請求會將請求參數(shù)放在請求體中,而GET請求則將請求參數(shù)放在URL中。通常情況下,POST請求比GET請求更安全,因為它可以隱藏請求參數(shù)。
我們通過requests.post()方法來發(fā)送一個POST請求,下面是詳細的代碼分析:
import requests
url = 'http://xxxx.org/post' # 這里使用xxxx.org來演示POST請求
data = {'key1': 'value1', 'key2': 'value2'}
response = requests.post(url, data=data)
print(response.text)
代碼解析
第一行導(dǎo)入了requests模塊,
第二行指定了要請求的URL地址 。
第三行定義了請求參數(shù)data,這個字典中包含了兩個鍵值對,分別表示key1和key2這兩個參數(shù)的值。第四行使用requests庫的post()方法來發(fā)送POST請求并獲取響應(yīng)對象。
我們通過data參數(shù)將請求參數(shù)放在請求體中,這里使用了字典類型作為請求參數(shù)。第五行使用print()函數(shù)打印出響應(yīng)內(nèi)容的文本形式。運行這段代碼,我們就可以在終端中看到xxxx.org返回的響應(yīng)內(nèi)容,其中包括了我們發(fā)送的請求參數(shù)。
在實際爬蟲中,我們可以利用requests模塊的一些屬性或者方法來解析響應(yīng)內(nèi)容,提取需要的數(shù)據(jù)。
發(fā)送JSON格式的POST請求
import requests
import json
url = 'http://xxxx.org/post' # 這里使用xxxx.org來演示POST請求
data = {'key1': 'value1', 'key2': 'value2'}
headers = {'Content-Type': 'application/json'}
response = requests.post(url, data=json.dumps(data), headers=headers)
print(response.text)
在這個案例中,我們將請求參數(shù)data轉(zhuǎn)換成JSON格式,并使用headers來指定Content-Type為application/json。然后,我們通過requests庫的post()方法來發(fā)送POST請求。
使用代理服務(wù)器發(fā)送POST請求
import requests
proxy = {
'http': 'http://10.10.1.10:3128',
'https': 'https://10.10.1.10:1080'
}
url = 'http://xxxx.org/post' # 這里使用xxxx.org來演示POST請求
data = {'key1': 'value1', 'key2': 'value2'}
response = requests.post(url, data=data, proxies=proxy)
print(response.text)
在這個案例中,我們通過proxy字典來指定代理服務(wù)器的地址和端口號。然后,我們通過requests庫的post()方法來發(fā)送POST請求。
發(fā)送帶文件的POST請求
import requests
url = 'http://xxxx.org/post' # 這里使用xxxx.org來演示POST請求
files = {'file': open('myfile.txt', 'rb')}
response = requests.post(url, files=files)
print(response.text)
在這個案例中,我們通過files參數(shù)來指定要上傳的文件。
open()函數(shù)打開文件,第一個參數(shù)是文件名,第二個參數(shù)是打開方式(rb表示二進制只讀模式)。然后,我們通過requests庫的post()方法來發(fā)送POST請求。
Xpath解析
XPath是一種用于選擇XML文檔中某些部分的語言。在Python中,我們可以使用lxml庫來解析XML文檔并使用XPath進行選擇。
XPath語法主要由路徑表達式和基本表達式構(gòu)成。其中,路徑表達式用于選擇節(jié)點或者節(jié)點集合,而基本表達式用于指定某個元素、屬性或者其他內(nèi)容。
XPath語法的規(guī)則集:
表達式 | 描述 |
---|---|
nodename | 選擇所有名為nodename的元素 |
/ | 從當(dāng)前節(jié)點選取根節(jié)點 |
// | 從當(dāng)前節(jié)點選取任意節(jié)點 |
. | 選擇當(dāng)前節(jié)點 |
… | 選擇當(dāng)前節(jié)點的父節(jié)點 |
@ | 選擇屬性 |
* | 匹配任何元素節(jié)點 |
[@attrib] | 選擇具有給定屬性的所有元素 |
[@attrib=‘value’] | 選擇具有給定屬性值的所有元素 |
tagname[text() = ‘text’] | 選擇具有給定文本的所有tagname元素 |
XPath解析的代碼案例及其詳細講解:
使用XPath解析HTML文檔
from lxml import etree
import requests
url = 'https://www.baidu.com'
html = requests.get(url).text
selector = etree.HTML(html)
result = selector.xpath('//title/text()')
print(result[0])
案例中,我們首先發(fā)送了一個GET請求獲取百度首頁的HTML源代碼。然后,我們使用lxml庫中的etree模塊來構(gòu)建一個XPath解析器,并將HTML源代碼傳給它進行解析。接著,我們使用XPath表達式’//title/text()'來選擇HTML文檔中title標(biāo)簽的內(nèi)容。最后,我們打印出XPath語句返回的結(jié)果。
使用XPath解析XML文檔
from lxml import etree
xml = '''
<bookstore>
<book category="cooking">
<title lang="en">Everyday Italian</title>
<author>Giammarco Tomaselli</author>
<year>2010</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>
</bookstore>
'''
selector = etree.XML(xml)
result = selector.xpath('//book[1]/title/text()')
print(result[0])
案例中,我們定義了一個XML字符串,并使用etree.XML()方法來創(chuàng)建一個XPath解析器。然后,我們使用XPath表達式’//book[1]/title/text()'來選擇XML文檔中第一個book元素的title元素的內(nèi)容。最后,我們打印出XPath語句返回的結(jié)果。
處理命名空間的XPath解析
from lxml import etree
xml = '''
<ns:bookstore xmlns:ns="http://www.example.com">
<ns:book category="cooking">
<ns:title lang="en">Everyday Italian</ns:title>
<ns:author>Giammarco Tomaselli</ns:author>
<ns:year>2010</ns:year>
<ns:price>30.00</ns:price>
</ns:book>
<ns:book category="children">
<ns:title lang="en">Harry Potter</ns:title>
<ns:author>J.K. Rowling</ns:author>
<ns:year>2005</ns:year>
<ns:price>29.99</ns:price>
</ns:book>
</ns:bookstore>
'''
selector = etree.XML(xml)
ns = {'ns': 'http://www.example.com'}
result = selector.xpath('//ns:book[1]/ns:title/text()', namespaces=ns)
print(result[0])
案例中,我們定義了一個帶有命名空間的XML字符串,并使用etree.XML()方法來創(chuàng)建一個XPath解析器。然后,我們通過傳遞一個namespaces參數(shù)來指定命名空間的前綴和URI。最后,我們使用XPath表達式’//ns:book[1]/ns:title/text()'來選擇第一個book元素的title元素的內(nèi)容。最后,我們打印出XPath語句返回的結(jié)果。
BeautifulSoup詳講與實戰(zhàn)
BeautifulSoup是常用的Python第三方庫,它提供了解析HTML和XML文檔的函數(shù)和工具。使用BeautifulSoup可以方便地遍歷和搜索文檔樹中的節(jié)點,獲取節(jié)點屬性和文本內(nèi)容等信息
創(chuàng)建BeautifulSoup對象
首先我們需要導(dǎo)入BeautifulSoup模塊:
from bs4 import BeautifulSoup
使用BeautifulSoup對HTML文檔進行解析,可以通過以下兩種方式:
(1) 傳遞一個HTML字符串作為參數(shù):
html_doc = """
<html>
<head><title>這是標(biāo)題</title></head>
<body>
<p class="para1">第一段落</p>
<p class="para2">第二段落</p>
</body>
</html>
"""
soup = BeautifulSoup(html_doc, 'html.parser')
(2) 傳遞一個文件路徑或文件對象作為參數(shù):
with open('example.html', 'r') as f:
soup = BeautifulSoup(f, 'html.parser')
遍歷文檔樹
很多時候,我們需要遍歷整個文檔樹來查找特定的節(jié)點,或者獲取節(jié)點的屬性和文本內(nèi)容。BeautifulSoup提供了多種遍歷文檔樹的方法,包括:
(1) .contents:返回一個包含所有子節(jié)點的列表。
for child in soup.body.contents:
print(child)
(2) .children:返回一個包含所有子節(jié)點的迭代器。
for child in soup.body.children:
print(child)
(3) .descendants:返回一個包含文檔樹中所有子孫節(jié)點的迭代器。
for element in soup.descendants:
print(element)
(4) .parent:返回一個節(jié)點的父節(jié)點。
p = soup.body.p
print(p.parent)
(5) .parents:返回一個包含節(jié)點所有祖先節(jié)點的迭代器。
p = soup.body.p
for parent in p.parents:
print(parent.name)
搜索文檔樹
搜索文檔樹是BeautifulSoup的另一個重點。BeautifulSoup提供了幾個搜索方法
(1) .find_all():返回一個滿足條件的節(jié)點列表。
soup.find_all('p', class_='para1')
soup.find_all('p', {'class': 'para1'}, string='第一段落')
(2) .find():返回第一個滿足條件的節(jié)點。
soup.find('p', class_='para1')
soup.find('p', {'class': 'para1'}, string='第一段落')
(3) .select():使用CSS選擇器語法返回滿足條件的節(jié)點列表。
soup.select('p.para1')
soup.select('p[class="para1"]')
獲取節(jié)點屬性和文本內(nèi)容
獲取節(jié)點的屬性和文本內(nèi)容也是常用的操作。BeautifulSoup提供了下面這些方法:
(1) .get():獲取節(jié)點的指定屬性。
p = soup.find('p', class_='para1')
print(p.get('class'))
(2) .text:獲取節(jié)點的文本內(nèi)容。
p = soup.find('p', class_='para1')
print(p.text)
(3) .string:獲取節(jié)點的文本內(nèi)容(如果節(jié)點只有一個子節(jié)點且該子節(jié)點是字符串類型)。
p = soup.find('p', class_='para1')
print(p.string)
BeautifulSoup 代碼案例講解。
解析HTML文檔并獲取標(biāo)題
from bs4 import BeautifulSoup
import requests
url = 'https://www.baidu.com'
html = requests.get(url).text
soup = BeautifulSoup(html, 'html.parser')
title = soup.title.string
print(title)
案例中,我們首先發(fā)送了一個GET請求獲取百度首頁的HTML源代碼。然后,我們使用BeautifulSoup來創(chuàng)建一個HTML解析器,并將HTML源代碼傳給它進行解析。接著,我們通過soup.title.string獲取HTML文檔中title標(biāo)簽的內(nèi)容,并打印出結(jié)果。
遍歷文檔樹并獲取所有段落內(nèi)容
from bs4 import BeautifulSoup
html_doc = """
<html>
<head><title>這是標(biāo)題</title></head>
<body>
<p class="para1">第一段落</p>
<p class="para2">第二段落</p>
</body>
</html>
"""
soup = BeautifulSoup(html_doc, 'html.parser')
for p in soup.body.children:
if p.name == 'p':
print(p.string)
案例中,我們創(chuàng)建了一個HTML字符串,并使用BeautifulSoup來創(chuàng)建一個HTML解析器。然后,我們通過soup.body.children遍歷整個文檔樹,查找所有的p標(biāo)簽,并打印出每個標(biāo)簽的文本內(nèi)容。
使用CSS選擇器搜索文檔樹
from bs4 import BeautifulSoup
html_doc = """
<html>
<head><title>這是標(biāo)題</title></head>
<body>
<p class="para1">第一段落</p>
<p class="para2">第二段落</p>
</body>
</html>
"""
soup = BeautifulSoup(html_doc, 'html.parser')
p_list = soup.select('p.para1')
for p in p_list:
print(p.text)
案例中,我們創(chuàng)建了一個HTML字符串,并使用BeautifulSoup來創(chuàng)建一個HTML解析器。然后,我們使用CSS選擇器’p.para1’搜索文檔樹,并獲取所有滿足條件的p標(biāo)簽。最后,我們遍歷p列表,并打印出每個標(biāo)簽的文本內(nèi)容。
好的,接下來我再給出三個代碼案例。
使用正則表達式搜索文檔樹
import re
from bs4 import BeautifulSoup
html_doc = """
<html>
<head><title>這是標(biāo)題</title></head>
<body>
<p class="para1">第一段落</p>
<p class="para2">第二段落</p>
</body>
</html>
"""
soup = BeautifulSoup(html_doc, 'html.parser')
pattern = re.compile('^p.*?1$') # 匹配所有以p開頭并且以1結(jié)尾的類名
p_list = soup.find_all(class_=pattern)
for p in p_list:
print(p.text)
案例中,我們使用了Python的re模塊來創(chuàng)建了一個正則表達式pattern。然后,我們使用soup.find_all(class_=pattern)來搜索文檔樹,獲取所有滿足條件的標(biāo)簽,并遍歷列表打印出每個標(biāo)簽的文本內(nèi)容。
解析XML文檔并獲取節(jié)點信息
from bs4 import BeautifulSoup
xml_doc = """
<?xml version="1.0" encoding="UTF-8"?>
<data>
<country name="Liechtenstein">
<rank>1</rank>
<year>2008</year>
<gdppc>141100</gdppc>
<neighbor name="Austria" direction="E"/>
<neighbor name="Switzerland" direction="W"/>
</country>
<country name="Singapore">
<rank>4</rank>
<year>2011</year>
<gdppc>59900</gdppc>
<neighbor name="Malaysia" direction="N"/>
</country>
</data>
"""
soup = BeautifulSoup(xml_doc, 'xml')
for country in soup.find_all('country'):
print(country['name'])
print(country.rank.text)
print(country.year.text)
print(country.gdppc.text)
for neighbor in country.find_all('neighbor'):
print(neighbor['name'], neighbor['direction'])
案例中,我們創(chuàng)建了一個XML字符串,并使用BeautifulSoup來創(chuàng)建一個XML解析器。然后,我們使用soup.find_all()方法搜索文檔樹,獲取所有滿足條件的標(biāo)簽,并遍歷它們打印出相關(guān)信息。
修改節(jié)點屬性
from bs4 import BeautifulSoup
html_doc = """
<html>
<head><title>這是標(biāo)題</title></head>
<body>
<p class="para1">第一段落</p>
<p class="para2">第二段落</p>
</body>
</html>
"""
soup = BeautifulSoup(html_doc, 'html.parser')
p = soup.find('p', class_='para1')
p['class'] = 'new_class'
print(p)
案例中,我們創(chuàng)建了一個HTML字符串,并使用BeautifulSoup來創(chuàng)建一個HTML解析器。然后,我們使用soup.find()方法搜索文檔樹,獲取第一個滿足條件的p標(biāo)簽。接著,我們通過p[‘class’]操作修改了標(biāo)簽的class屬性,并打印出修改后的標(biāo)簽。
正則表達式
正則表達式知識點
正則表達式是一種用于匹配字符串的模式。它通過字符組成規(guī)則定義了搜索文本中特定模式的方法。Python中的re模塊提供了使用正則表達式的功能。
常用的正則表達式元字符:
- . 表示任意字符。
- \d表示數(shù)字,\D表示非數(shù)字。
- \w表示單詞字符,即az、AZ、0~9和下劃線。
- \W表示非單詞字符。
- \s表示空白符,包括空格、制表符、換行符等。
- \S表示非空白符。
- ^表示匹配行首。
- $表示匹配行尾。
- *表示匹配前面的字符零次或多次。
- +表示匹配前面的字符一次或多次。
- ?表示匹配前面的字符零次或一次。
- {m}表示匹配前面的字符m次。
- {m,n}表示匹配前面的字符m到n次。
- […]表示匹配方括號中任意一個字符。
- [^…]表示匹配除了方括號中給出的字符以外的任意一個字符。
- (…)表示匹配括號中的表達式。
re模塊中常用的函數(shù):
- re.match():從字符串的開頭開始匹配,只匹配一次。
- re.search():在字符串中匹配第一個符合條件的內(nèi)容。
- re.findall():在字符串中匹配所有符合條件的內(nèi)容并以列表的形式返回。
- re.sub():用一個新的字符串替換掉匹配到的所有內(nèi)容。
- re.compile():將正則表達式轉(zhuǎn)化為一個正則表達式對象,以便于復(fù)用。
正則表達式案例
(1) 匹配手機號碼
import re
phone_nums = ['13912345678', '13812345678', '13512345678', '13612345678', '13712345678']
pattern = r'^1[3-9]\d{9}$'
for phone_num in phone_nums:
if re.match(pattern, phone_num):
print(f'{phone_num}是一個合法的手機號碼')
else:
print(f'{phone_num}不是一個合法的手機號碼')
代碼演示了如何使用正則表達式匹配手機號碼。
首先,我們定義了一個包含多個手機號碼的列表,并創(chuàng)建了一個正則表達式對象pattern。該正則表達式匹配以1開頭的11位數(shù)字字符串,其中第二位數(shù)字介于3和9之間。然后,我們使用re.match()方法對每個手機號碼進行匹配,并打印結(jié)果。
(2) 替換HTML文檔中的標(biāo)簽
import re
html_doc = """
<html>
<head><title>這是標(biāo)題</title></head>
<body>
<p class="para1">第一段落</p>
<p class="para2">第二段落</p>
</body>
</html>
"""
pattern = r'<.*?>'
new_doc = re.sub(pattern, '', html_doc)
print(new_doc)
代碼演示了如何使用正則表達式替換HTML文檔中的標(biāo)簽。
首先,我們定義了一個包含HTML標(biāo)簽的字符串,并創(chuàng)建了一個正則表達式對象pattern。該正則表達式匹配任意HTML標(biāo)簽,并將其替換為空字符串。然后,我們使用re.sub()方法對HTML文檔進行替換,并打印結(jié)果。
(3) 提取金融數(shù)據(jù)
import re
text = '2019年GDP增速為7.5%,同比增長0.3個百分點;CPI同比上漲2.5%,環(huán)比上漲0.3%。'
pattern1 = r'\d+.\d+%'
pattern2 = r'[A-Z]+'
num_list = re.findall(pattern1, text)
unit_list = re.findall(pattern2, text)
for i in range(len(num_list)):
print(f'{num_list[i]} {unit_list[i]}')
代碼演示了如何使用正則表達式提取金融數(shù)據(jù)。
首先,我們定義了一個包含金融數(shù)據(jù)的字符串,并創(chuàng)建了兩個正則表達式對象pattern1和pattern2。其中,pattern1匹配百分?jǐn)?shù),pattern2匹配單位符號。然后,我們使用re.findall()方法分別提取百分?jǐn)?shù)和單位符號,并以列表的形式返回。最后,我們使用for循環(huán)遍歷兩個列表,并將相同位置上的元素打印在一起。
正則表達式實戰(zhàn)
代碼是一個簡單的Python腳本,可以用于統(tǒng)計某個文件夾下所有文本文件中各個單詞的出現(xiàn)頻率,并輸出前十個出現(xiàn)頻率最高的單詞及其出現(xiàn)次數(shù)。在代碼中,我們將使用正則表達式來去除標(biāo)點符號、換行符等非單詞字符,以便于單詞的準(zhǔn)確統(tǒng)計。
import os
import re
from collections import Counter
def get_word_counts(folder_path):
"""
統(tǒng)計指定文件夾中所有文本文件中各個單詞的出現(xiàn)頻率,并返回一個Counter對象。
"""
word_counter = Counter()
for root, dirs, files in os.walk(folder_path):
for file in files:
if file.endswith('.txt'):
file_path = os.path.join(root, file)
# 讀取文本文件內(nèi)容
with open(file_path, 'r', encoding='utf-8') as f:
text = f.read()
# 使用正則表達式去除標(biāo)點符號、換行符等非單詞字符
pattern = r'\b\w+\b'
words = re.findall(pattern, text)
# 對單詞列表進行計數(shù),并將結(jié)果更新到Counter對象中
word_counter.update(words)
return word_counter
if __name__ == '__main__':
folder_path = 'test'
word_counter = get_word_counts(folder_path)
# 輸出前十個出現(xiàn)頻率最高的單詞及其出現(xiàn)次數(shù)
top_n = 10
print(f'Top {top_n} words:')
for word, count in word_counter.most_common(top_n):
print(f'{word:<10} {count}')
代碼中的
get_word_counts()
函數(shù)用于統(tǒng)計指定文件夾中所有文本文件中各個單詞的出現(xiàn)頻率,并返回一個Counter對象。在函數(shù)中,我們使用了Python內(nèi)置的os
和collections
模塊,以便于對文件和單詞計數(shù)進行操作。os.walk()
方法可以遍歷指定文件夾下所有子文件夾中的文件,比如我們指定的folder_path
文件夾。然后,我們對每個文本文件進行讀取,并使用正則表達式去除標(biāo)點符號、換行符等非單詞字符,以便于單詞的準(zhǔn)確統(tǒng)計。最后,我們使用Counter
對象來對單詞列表進行計數(shù),并將結(jié)果更新到該對象中。
在主程序中,我們調(diào)用
get_word_counts()
函數(shù)來獲取單詞計數(shù)結(jié)果,并輸出前十個出現(xiàn)頻率最高的單詞及其出現(xiàn)次數(shù)。在這里,我們使用了most_common()
方法來獲取前N個出現(xiàn)頻率最高的單詞及其出現(xiàn)次數(shù),并使用字符串格式化輸出結(jié)果。
字體反爬
字體反爬是一種常見的網(wǎng)站反爬手段,即將大部分文本內(nèi)容通過特定的字體進行加密混淆,以防止爬蟲直接抓取數(shù)據(jù)。通常情況下,爬蟲需要先解密字體,然后才能正常獲取到文本內(nèi)容。
常用的字體反爬解密方法有以下幾種:
- 解析woff文件
很多網(wǎng)站會使用woff格式的字體文件來渲染文本內(nèi)容,爬蟲需要先下載這些字體文件,并解析出字符與字形之間的對應(yīng)關(guān)系,然后才能正常解密文本內(nèi)容。
- 使用fontTools庫
Python中有一個非常優(yōu)秀的字體解析庫叫做fontTools,可以幫助我們輕松地解析字體文件,并生成字形對應(yīng)表。使用該庫可以避免自行解析字體文件所遇到的各種問題。
- 使用在線字體解密工具
有些網(wǎng)站提供了在線字體解密工具,如FontSpider、字體反爬插件等,可以幫助我們快速地解密字體。不過,使用這種方法需要注意隱私安全問題。
(1) 解析woff文件
import base64
from fontTools.ttLib import TTFont
# 下載字體文件并保存為base64編碼字符串
font_url = 'http://example.com/font.woff'
font_base64 = '...'
# 將base64編碼字符串解碼并保存到本地
with open('font.woff', 'wb') as f:
font_data = base64.b64decode(font_base64)
f.write(font_data)
# 解析字體文件并獲取字形對應(yīng)表
font = TTFont('font.woff')
cmap = font.getBestCmap()
# 定義替換規(guī)則
replace_dict = {
'连': '0',
'': '1',
'﫵': '2',
'': '3',
'': '4',
'': '5',
'': '6',
'': '7',
'倫': '8',
'': '9',
}
# 替換文本內(nèi)容
text = ''
for key, value in replace_dict.items():
text = text.replace(key, value)
# 輸出結(jié)果
print(text)
代碼演示了如何解析woff文件,并使用字形對應(yīng)表來解密文本內(nèi)容。首先,我們將從網(wǎng)站上下載字體文件,并保存為base64編碼字符串。然后,我們將該編碼字符串解碼并保存到本地。接下來,我們使用fontTools庫讀取字體文件,并獲取其中的字形對應(yīng)表。需要注意的是,不同字體文件對應(yīng)的字形對應(yīng)表可能不同,因此需要根據(jù)具體情況來確定使用哪個表。
我們定義了一個替換規(guī)則字典replace_dict,其中包含了從未解密的字符到明文字符的映射關(guān)系。最后,我們使用字符串的replace()方法將未解密的文本內(nèi)容替換為明文,從而得到結(jié)果。
(2) 使用fontTools庫
import requests
from io import BytesIO
from fontTools.ttLib import TTFont
# 下載字體文件并用fontTools庫讀取
font_url = 'http://example.com/font.woff'
font_data = requests.get(font_url).content
font = TTFont(BytesIO(font_data))
# 獲取字形對應(yīng)表
cmap = font.getBestCmap()
# 定義替換規(guī)則
replace_dict = {
'连': '0',
'': '1',
'﫵': '2',
'': '3',
'': '4',
'': '5',
'': '6',
'': '7',
'倫': '8',
'': '9',
}
# 解密文本內(nèi)容
text = ''
for key, value in replace_dict.items():
glyph_id = cmap[int(key[3:-1], 16)]
text = text.replace(key, value)
# 輸出結(jié)果
print(text)
代碼演示了如何使用fontTools庫解析字體文件,并生成字形對應(yīng)表。首先,我們使用requests庫從網(wǎng)站上下載字體文件,并使用BytesIO將字節(jié)流轉(zhuǎn)換為文件。然后,我們使用fontTools庫讀取該文件,并獲取其中的字形對應(yīng)表。需要注意的是,通過這種方式獲取到的字形對應(yīng)表可能與其他方式獲取到的表略有不同,因此需要進行實驗來確定使用哪個表。
我們定義了一個替換規(guī)則字典replace_dict,并使用字符串的replace()方法將未解密的文本內(nèi)容替換為明文,從而得到結(jié)果。
(3) 使用在線字體解密工具
import requests
from fontSpider import FontSpider
# 下載字體文件并保存為base64編碼字符串
font_url = 'http://example.com/font.woff'
r = requests.get(font_url)
font_base64 = FontSpider(r.content).to_base64()
# 使用在線字體解密工具解密文本內(nèi)容
text = ''
response = requests.post('http://font-spider.com/api/v1/decrypt', data={'font': font_base64, 'text': text})
result = response.json()
# 輸出結(jié)果
print(result['data'])
代碼演示了如何使用在線字體解密工具來解密文本內(nèi)容。首先,我們從網(wǎng)站上下載字體文件,并使用FontSpider庫將其轉(zhuǎn)換為base64編碼字符串。然后,我們使用requests庫向在線字體解密工具發(fā)送POST請求,并將字體文件和未解密的文本內(nèi)容作為參數(shù)傳遞。該工具會自動解密文本內(nèi)容,并返回解密后的結(jié)果。最后,我們從響應(yīng)結(jié)果中提取出解密后的文本內(nèi)容,并輸出結(jié)果。
需要注意的是,使用在線字體解密工具可能存在隱私安全問題,因此盡量避免在生產(chǎn)環(huán)境中使用。
Scrapy入門
Scrapy是一個基于Python的快速、高效的Web爬蟲框架,可用于數(shù)據(jù)抓取、信息處理以及存儲的開發(fā)。它是一個專業(yè)的爬蟲框架,提供了許多必要的功能,如請求調(diào)度、數(shù)據(jù)解析,以及數(shù)據(jù)存儲等。Scrapy可以自動下載網(wǎng)頁,并提供了XPath以及CSS選擇器等多種方法,支持多線程和分布式爬取,并可以通過插件擴展其功能。
- 工程結(jié)構(gòu)
Scrapy的工程具有標(biāo)準(zhǔn)的項目結(jié)構(gòu),通常包含以下幾個文件:
- scrapy.cfg:Scrapy項目配置文件。
- items.py:定義爬取的數(shù)據(jù)結(jié)構(gòu)。
- middlewares.py:管理請求和響應(yīng),例如User-Agent、代理等。
- pipelines.py:配置到底怎么樣后續(xù)處理item。
- settings.py:保存爬蟲的參數(shù)設(shè)置。
- spiders/:保存爬蟲代碼的目錄。
- 爬蟲流程
Scrapy的爬蟲流程如下:
- 發(fā)起請求:通過定義好的URL地址來發(fā)送HTTP請求。
- 下載頁面:Scrapy會自動下載對應(yīng)的頁面,或使用第三方庫,如requests、Selenium等。
- 解析頁面:使用XPath或CSS選擇器解析網(wǎng)頁內(nèi)容。
- 保存數(shù)據(jù):將解析得到的數(shù)據(jù)保存到本地或數(shù)據(jù)庫中。
- Scrapy組件
Scrapy具有以下幾個重要組件:
- Spider:定義如何抓取某個站點,包括如何跟進鏈接、如何分析頁面內(nèi)容等。
- Item:定義爬取的數(shù)據(jù)結(jié)構(gòu)。
- Pipeline:負責(zé)處理Item,如清理、過濾、存儲到數(shù)據(jù)庫等。
- Downloader:負責(zé)下載網(wǎng)頁,并將結(jié)果傳遞給Spider。
- Scheduler:負責(zé)調(diào)度Spider發(fā)起請求,并將結(jié)果傳遞給Downloader。
Scrapy實例
- 爬取豆瓣電影TOP250的數(shù)據(jù)
import scrapy
class DoubanMovieSpider(scrapy.Spider):
name = 'douban_movie'
allowed_domains = ['movie.douban.com']
start_urls = ['https://movie.douban.com/top250']
def parse(self, response):
for info in response.xpath('//div[@class="info"]'):
yield {
'title': info.xpath('div[@class="hd"]/a/span/text()').extract_first(),
'score': info.xpath('div[@class="bd"]/div[@class="star"]/span[@class="rating_num"]/text()').extract_first(),
'director': info.xpath('div[@class="bd"]/p/text()')[0].strip() if len(info.xpath('div[@class="bd"]/p')) == 2 else '',
'year': info.xpath('div[@class="bd"]/p/text()')[-1].strip().replace('(', '').replace(')', '') if len(info.xpath('div[@class="bd"]/p')) == 2 else info.xpath('div[@class="bd"]/p/text()')[-1].strip()
}
next_page = response.xpath('//span[@class="next"]/a/@href')
if next_page:
yield scrapy.Request(url=response.urljoin(next_page.extract_first()), callback=self.parse)
代碼演示了如何使用Scrapy爬取豆瓣電影TOP250的數(shù)據(jù)。
首先,我們定義了一個名為DoubanMovieSpider的爬蟲類,并設(shè)置了訪問URL。在parse()函數(shù)中,我們首先使用XPath選擇器來解析電影數(shù)據(jù),然后通過yield關(guān)鍵字返回一個Python字典,字典的鍵是電影標(biāo)題、評分、導(dǎo)演和年份。接著,我們使用XPath選擇器獲取下一頁的鏈接,并使用yield關(guān)鍵字發(fā)送一個HTTP請求,進入下一頁繼續(xù)執(zhí)行parse()函數(shù)。
- 使用middlewares設(shè)置User-Agent
import random
from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware
class RandomUserAgentMiddleware(UserAgentMiddleware):
user_agents = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299',
'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36',
'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36',
'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36',
'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; AS; rv:11.0) like Gecko',
'Mozilla/5.0 (Windows NT 6.3; WOW64; rv:57.0) Gecko/20100101 Firefox/57.0',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299'
]
def process_request(self, request, spider):
user_agent = random.choice(self.user_agents)
request.headers.setdefault('User-Agent', user_agent)
代碼演示了如何使用middlewares設(shè)置User-Agent。我們首先創(chuàng)建RandomUserAgentMiddleware類,并繼承自Scrapy提供的UserAgentMiddleware類,然后定義user_agents列表,保存多種User-Agent。接著,我們重載process_request()函數(shù),并隨機選擇一個User-Agent,將其添加到HTTP請求頭中。
- 將數(shù)據(jù)寫入MySQL數(shù)據(jù)庫
import pymysql
from scrapy.exceptions import DropItem
class MysqlPipeline(object):
def __init__(self, mysql_host, mysql_db, mysql_user, mysql_pwd, mysql_table):
self.mysql_host = mysql_host
self.mysql_db = mysql_db
self.mysql_user = mysql_user
self.mysql_pwd = mysql_pwd
self.mysql_table = mysql_table
def process_item(self, item, spider):
if item['title'] and item['score']:
db = pymysql.connect(host=self.mysql_host, user=self.mysql_user, password=self.mysql_pwd, db=self.mysql_db)
cursor = db.cursor()
sql = "INSERT INTO %s (title, score, director, year) VALUES ('%s', '%s', '%s', '%s')" % (self.mysql_table, item['title'], item['score'], item['director'], item['year'])
try:
cursor.execute(sql)
db.commit()
except Exception as e:
db.rollback()
raise DropItem("Failed to insert item: {}".format(e))
finally:
db.close()
return item
代碼演示了如何將數(shù)據(jù)寫入MySQL數(shù)據(jù)庫。我們首先定義了一個名為MysqlPipeline的類,并繼承自一個Scrapy提供的基本管道類。在__init__()函數(shù)中,我們從配置文件或命令行參數(shù)中獲取MySQL的連接參數(shù),包括主機、數(shù)據(jù)庫名、用戶名、密碼以及數(shù)據(jù)表名。在process_item()函數(shù)中,我們判斷需要保存的數(shù)據(jù)是否為空,并使用pymysql庫連接數(shù)據(jù)庫。然后,我們執(zhí)行SQL插入語句,并在發(fā)生錯誤時進行回滾操作。最后,在finally中關(guān)閉數(shù)據(jù)庫連接。文章來源:http://www.zghlxwxcb.cn/news/detail-496177.html
完結(jié)
文章來源地址http://www.zghlxwxcb.cn/news/detail-496177.html
到了這里,關(guān)于Python網(wǎng)絡(luò)爬蟲基礎(chǔ)進階到實戰(zhàn)教程的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!