爬蟲基礎(二)——網頁

来源:https://www.cnblogs.com/ydkh/archive/2018/11/25/10012790.html
-Advertisement-
Play Games

前言 爬蟲要爬取的信息主要來自於網頁載入的內容,有必要瞭解一些網頁的知識。 當我們在瀏覽器網址欄輸入一個網址——URL,經過TCP/IP協議簇的處理,這個網址請求的信息就被髮送到URL對應的伺服器,接著伺服器處理這個請求,並將請求的內容返回給瀏覽器,瀏覽器便顯示或者下載URL請求相應的資源。這是前一 ...


前言

  爬蟲要爬取的信息主要來自於網頁載入的內容,有必要瞭解一些網頁的知識。

  當我們在瀏覽器網址欄輸入一個網址——URL,經過TCP/IP協議簇的處理,這個網址請求的信息就被髮送到URL對應的伺服器,接著伺服器處理這個請求,並將請求的內容返回給瀏覽器,瀏覽器便顯示或者下載URL請求相應的資源。這是前一篇博客所述。

  在這一篇博客,筆者嘗試說明瀏覽器是如何顯示出這個頁面的。如下

HTML

HTML的含義

  與超文本相對的是線性文本。線性,即直線關係,成比例。一本書,從第一頁到最後一頁,呈現直線關係;一本書的書簽,從第一章轉跳至第十章,呈現的是非線性關係。對於線性的電腦文件,不能直接從從一個位置的文件非線性地轉至另一個位置的文件,這中間是要經過一定的順序;相反,超文本之間的關係是非線性的,從一個HTML文件可以直接連接至另一個HTML文件。促成這種連接的正是是超文本鏈接,超文本鏈接就是超鏈接,上一篇的URL就是超鏈接的一種,電子書中的書簽也是超鏈接的一種。

  HTML是一門語言,常用於編寫網頁,HTML文件是超文本的一種形式。以下是一些名稱的解釋,以輔助理解,不必太在意於嚴格的定義。

  • HTML(HyperText Mark-up Language):超文本標記語言
  • 超文本:HyperText,用超鏈接的方法,將不同空間的文字信息組織在一起的網狀文本
  • 鏈接:link,從一個文檔指向其它文檔或從文本錨點(anchor)指向某已命名位置的鏈接
  • 錨點:anchor,是網頁製作中超級鏈接的一種,又叫命名錨記。命名錨記像一個迅速定位器一樣是一種頁面內的超級鏈接
  • 超鏈接:hyperlink,它是一種允許我們同其他網頁或站點之間進行連接的頁面元素
  • 超文本鏈接:Hypertext link,就是超鏈接。是指用文字鏈接的形式來指向一個頁面
  • 線性:linear,指量與量之間按比例、成直線的關係,在數學上可以理解為一階導數為常數的函數

樹的概念

  樹的結構是很簡單的,平時留心觀察即可知道樹為何是“直”的。從第一個分叉開始這樹就是由無數的“開叉”結構組成,直至最微小的枝芽。怎麼簡單怎麼來,數學上的描述不管。下麵的性質和定義來自《用Python解決數據結構和演算法》

樹的性質

     相關術語在“定義1”裡面有解釋,以分類樹為例此處有圖片

  1. 樹是分層的,分層的意思是樹的頂層部分更加寬泛一般而底層部分更加精細具體。在圖1中,最上層是“界”,它下麵的一層(上層的子層)是“門”,然後是“綱”等等。
  2. 一個節點的子節點(node)和另一個節點的子節點(children)是完全獨立的。如圖1,“貓屬”有兩個子節點“家生”和“野生”,“蠅屬”中也有一個“家生”, 但它和“貓屬”中的“家生”是完全不同而且相互獨立的。
  3. 樹的每個葉節點(leaf)都是不同的。如圖1,對每一種動物,我們都可以從根節點(root)開始沿著一條特定的路徑找到它對應的葉節點,並把它和其他動物區分開, 例如對於家貓
  4. 樹下層的所有部分(子樹Subtree)移動到樹的另一位置而不影響更下層的情況。如圖2,我們可以將所有標註/etc的子樹從根節點下移動到usr/下麵但是對httpd的內容及其子節點的內容不會有影響。

圖1 一些動物的分類樹

圖2 一小部分Unix文件系統的分層情況 

定義1

  樹是節點和連接節點的邊的集合

  這個定義簡單粗暴,但蘊含的東西不少。以下是一些相關的東西,都是些抽象的概念,將其類比成枝節葉可以吧

  • 節點(Node):樹的基本組成
  • 邊(Edge):樹的基本組成,連接兩個節點。。每個節點(除了根節點)都有且只有一條與其他節點相連的入邊(指向該節點的邊),每個節點可能有許多條出邊(從該節點指向其他節點的邊)。
  • 根節點(Root):樹中唯一沒有入邊的節點
  • 路徑(Path):路徑是由邊連接起來的節點的有序排列
  • 子節點集(Childern):當一個節點的入邊來自於另外一個節點時,稱前者為後者的子節點。同一個節點的所有子節點構成子節點集
  • 父節點(Parent):一個節點是它的所有出邊連接的節點的父節點。
  • 兄弟節點(Sibling)同一節點的所有子節點胡偉兄弟節點
  • 子樹(Subtree):子樹是一個父節點的某個子節點的所有邊和後代節點所構成的集合
  • 葉節點(LeafNode):沒有子節點的節點稱為葉節點
  • 層數(Level):一個節點的層數是指從跟節點到該節點的路徑的邊的數目,定義根節點層數為0
  • 高度(Height):樹的高度等於所有節點層數的最大值

定義2

  每棵樹為空,或者包含一個根節點和0個或多個子樹,其中每個子樹也符合這樣的定義

  這個定義巧妙,用到遞歸只能“巧妙”了。

HTML的構成

  HTML是由一系列的元素組成,元素由首尾標簽和其中的內容組成,學習HTML就要學習那一堆元素。標簽表示元素的起始和結束。下麵是一個簡單的HTML網頁。例如代下麵代碼中

<li>List item one</li>是元素,<li>是首標簽,</li>是尾標簽,'List item one'是內容。

 

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>simple</title>
</head>
<body>
<h1>A simple web page</h1>
<ul>
<li>List item one</li>
<li>List item two</li>
</ul>
<h2><a href="http://www.cs.luther.edu">Luther CS </a><h2>
</body>
</html>

代碼1

  這個網頁也相當於一棵樹,樹的每一層都對應超文本標記符的一層嵌套。如圖3

圖3 與網頁的構成元素相對應的樹

DOM

  DOM(Document Object Model),文檔對象模型。當瀏覽器要顯示HTML文檔網頁的時候,瀏覽器會創建這個網頁全部元素的內部表示體系——DOM,類似於地圖表示實際的地點一樣,DOM也可以看做是這個HTML網頁的“地圖”,我們可以通過JavaScript(例如父子對象的形式)去讀取DOM這張“地圖”。在DOM裡面,網頁的所有元素以父子對象等形式形成樹形結構,這棵樹最頂層的是瀏覽器window對象(如圖4),window對象的一個子對象是document對象,一個HTML文檔被載入到瀏覽器的時候,都會創建一個document對象,這個對象包含了HTML文檔的全部元素,同樣HTML的內容也會表示成樹形結構(如圖3)

  當DOM把網頁表示成“樹”的形式(如圖3)時,每個元素都相當於樹的節點(元素節點),每個屬性也相當一個節點(屬性節點),文本也是(文本節點),屬性節點和文本節點包含在元素節點中。邊表示了元素間的關係。

圖4 window對象及其一些子對象

CSS

  通過DOM模型,瀏覽器就知道如何去顯示一個HTML網頁的title,h1,body,ul······,但這並不是唯一的方式,我們同樣可以通過CSS(Cascading Style Sheets)層級樣式表去告訴瀏覽器該如何去顯示一個網頁文檔,實際上瀏覽器也會根據外部樣式表去構建一棵“樹”——CSSOM(CSS Object Model,CSS 對象模型)。

  CSS是一種樣式表語言,用於為HTML文檔定義佈局。例如,設置字體、顏色、邊距、高度、寬度、背景圖像等等。爬蟲中經常用到CSS選擇器。

添加CSS的方法

行內樣式表

  為HTML應用CSS的一種方法是使用HTML屬性style。例如下麵代碼,通過行內樣式表將頁面背景設為紅色,代碼如下:

<html>
<head>
<title>例子</title> 
</head>
<body style="background-color: #FF0000;"> 
<p>這個頁面是紅色的</p>
</body>
</html>

內部樣式表

  為HTML應用CSS的另一種方法是採用HTML元素style。代碼如下

<html> 
<head> 
<title>例子</title>
    <style type="text/css">
     body {background-color: #FF0000;}        
     </style> 
</head> 
<body> <p>這個頁面是紅色的</p> 
</body>
</html>

外部樣式表

  外部樣式表就是一個擴展名為css的文本文件。如何在一個HTML文檔里引用一個外部樣式表文件(style.css)呢?可以在HTML文檔里創建一個指向外部樣式表文件的鏈接(link)即可,就像下麵代碼那樣,其中href="style/style.css是CSS文件的路徑,要註意的就是外部樣式表的路徑問題,詳略。 代碼如下:

<link rel="stylesheet" type="text/css" href="style/style.css" />

CSS構造樣式規則

  樣式表中包含了定義網頁外觀的規則,樣式表中的每條規則都有兩個主要部分:選擇器(selector)和聲明塊(declaration block)。選擇器的作用在於定位以及決定哪些元素受到影響;聲明塊由一個或多個屬性- 值對(每個屬性-值對構成一條聲明,declaration)組成,它們指定應該做什麼(參見圖5 ~圖6)。

  構造樣式規則的步驟如下:

  1. 輸入selector ,這裡的selector 表示希望進行格式化的元素。
  2. 輸入{(前花括弧)開始聲明塊。
  3. 輸入property:value; ,其中property是CSS 屬性的名稱,描述要應用哪種格式;value 是該屬性允許的選項之一。
  4. 根據需要,重覆第(3) 步。通常一行輸入一個property: value(一條聲明),如圖6所示的那樣,但這並非強制要求。
  5. 輸入},結束聲明塊和樣式規則。

CSS選擇器

  由於選擇器具有定位作用,例如所以利用選擇器就可以定位到我們想提取的數據,因此,CSS選擇器經常在爬蟲中出現。常見的CSS選擇器語法規則如圖7,見W3C鏈接

圖7 一些CSS選擇器的語法規則

CSS選擇器的應用

在Beautiful Soup中的應用

  例如如果爬取到下麵這段HTML代碼,就可以通過CSS選擇器去提取,如下:

html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><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>
"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html_doc, 'lxml')

# 選擇所有title標簽,結果是一個列表,可迭代
print(soup.select("title"))
# 選擇body標簽下的所有a標簽,並獲取文本
results = soup.select("body a")
for result in results:
    print(result.get_text())
# 通過id查找 選擇a標簽,其id屬性為link1的標簽
print(soup.select("a#link1"))
# 選擇所有p標簽中的第三個標簽
print(soup.select("p:nth-of-type(3)"))     # 相當於soup.select(p)[2]
# 選擇a標簽,其href屬性以lacie結尾
print(soup.select('a[href$="lacie"]'))
# 選擇a標簽,其href屬性包含.com
print(soup.select('a[href*=".com"]'))
# 通過【屬性】查找,選擇a標簽,其屬性中存在myname的所有標簽
a = soup.select("a[myname]")
# 選擇a標簽,其屬性href=http://example.com/lacie的所有標簽
b = soup.select("a[href='http://example.com/lacie']")
# 選擇a標簽,其href屬性以http開頭
c = soup.select('a[href^="http"]')
print(a)
print(b)
print(c)
 1 # 選擇body標簽下的直接a子標簽
 2 print(soup.select("body > a"))
 3 # 選擇id=link1後的所有兄弟節點標簽
 4 print(soup.select("#link1~.mysis"))
 5 # 選擇id=link1後的下一個兄弟節點標簽
 6 print(soup.select("#link1 + .mysis"))
 7 # 選擇a標簽,其類屬性為mysis的標簽
 8 print(soup.select("a.mysis"))
 9 # 從html中排除某標簽,此時soup中不再有script標簽
10 print([s.extract()for s in soup('script')])
11 # 如果想排除多個呢
12 print([s.extract()for s in soup(['script', 'fram'])])
View Code

在pyquery中的應用

  例如如果爬取到下麵這段HTML代碼,就可以通過CSS選擇器去提取,如下:

html = '''
<div class="wrap">
    <div id="container">
        <ul class="list">
             <li class="item-0">first item</li>
             <li class="item-1"><a href="link2.html">second item</a></li>
             <li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
             <li class="item-1 active"><a href="link4.html">fourth item</a></li>
             <li class="item-0"><a href="link5.html">fifth item</a></li>
         </ul>
     </div>
 </div>
'''
from pyquery import PyQuery as pq
doc = pq(html)
a = doc('.item-0.active a')  # 先獲取class為item-0 且class為active的li標簽內的a標簽節點,再提取屬性
print(a, type(a))
print(a.attr('href'))         # 獲取到的結果為鏈接路徑: link3.html
print(a.attr.href)
print(a.text())                # 獲取文本,獲得a節點的wb
li = doc('.item-0.active')
print(li.html())               # html()返回該節點的所有文本,包括標簽a的開始和結束
lt = doc('li')
print(lt.html())               # 只返回第一個li的文本,欲獲取全部需要遍歷
print(lt.text())               # 返回所有li的文本,用空格隔開,結果是字元串類型
print(type(lt.text()))
b = doc('a')
print(b, type(b))
print(b.attr.href)  # attr()方法只會得到第一個節點的屬性,這時,需要遍歷
for item in b.items():
    print(item.attr.href)
html = '''
<div id="container">
    <ul class="list">
         <li class="item-0">first item</li>
         <li class="item-1"><a href="link2.html">second item</a></li>
         <li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
         <li class="item-1 active"><a href="link4.html">fourth item</a></li>
         <li class="item-0"><a href="link5.html">fifth item</a></li>
     </ul>
 </div>
'''
from pyquery import PyQuery as pq
doc = pq(html)
print(doc('#container .list li'))
print(type(doc('#container .list li')))
items = doc('.list')
print(items.text())
print(type(items.text()))
print(items)
lis = items.find('li')
print(lis)
print(type(items))
print(type(lis))
ls = items.children()  # 返回子節點
print(ls)
print(type(ls))
View Code

JavaScript

  這裡只說兩點,ajax和渲染,因為爬蟲經常碰到

渲染——瀏覽器如何顯示頁面

  到目前為止,已經瞭解到瀏覽器在載入HTML的時候,先解析HTML文檔,然後生成HTML樹——DOM,同時瀏覽器生成了另外一棵樹——CSSOM,這兩個模型共同創建“渲染樹”,之後瀏覽器就有了足夠的信息去進行佈局,併在屏幕上繪製頁面。如果這裡沒有外部樣式表也沒有行內或者內部樣式表(前面所述),也無需操心,因為瀏覽器本身也自帶了一個預設的CSS樣式表,只不過我們自定義的CSS樣式表會將它覆蓋而已。這裡的“繪製的頁面”就是要顯示的頁面,暫且理解成編程中的“print”吧,這裡的一些奇怪的問題(比如:“瀏覽器顯示HTML文檔首尾標簽去哪裡啦?)”都可以類比print函數中的一些問題(“引號去哪裡了?”)來看待,因為瀏覽器的顯示和print函數是的目的都是將內容顯示到電腦屏幕!只不過這裡的繪製不是普通列印而是“彩打”

渲染的過程如下(圖片來自這裡):

   為什麼渲染還和JavaScript有關呢?是的,單單是HTML和CSS就可以顯示出網頁,但JavaScript卻有更強大的功能,其實JavaScript就是網頁源代碼中的一個腳本,他在瀏覽器顯示頁面的時候可以改變這個頁面的佈局和內容,也就是改變DOM和CSSOM的能力,從而改變了網頁的顯示。 

ajax

  Ajax是一種無需刷新頁面即可從伺服器(或客戶端)上載入數據的手段,這裡的刷新是指重新請求,重新下載頁面。而Ajax卻可以在不刷新的情況下載入數據,從而給人一種“流暢”的感覺。但ajax只是其中的一種手段,例如上面提到的JavaScript渲染也是這樣的一種手段。那麼ajax是如何實現這種效果的呢?既然載入了數據那麼肯定是向伺服器發送了請求,那麼如何做到不顯示新的頁面呢?答案是XMLHttpRequest(XHR)對象,它可以實現這種方式。既然是對象當然就有類似於“send()”等方法向伺服器發送請求,然後接受到伺服器響應的內容,接下來avaScript就會解釋並處理這些內容,然後渲染網頁,繼而瀏覽器將數據顯示出來。因此在爬蟲的時候要想爬取這種動態載入的數據,就需要在開發者工具中去找尋這些新的URL請求,然後再在程式中模擬這種請求,再提取數據。就這樣先吧。代碼來自W3C如下:

<html>
<head>
<script type="text/javascript">
function loadXMLDoc()
{
var xmlhttp;
if (window.XMLHttpRequest)
  {// code for IE7+, Firefox, Chrome, Opera, Safari
  xmlhttp=new XMLHttpRequest();
  }
else
  {// code for IE6, IE5
  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }
xmlhttp.onreadystatechange=function()
  {
  if (xmlhttp.readyState==4 && xmlhttp.status==200)
    {
    document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
    }
  }
xmlhttp.open("POST","/ajax/demo_post.asp",true);
xmlhttp.send();
}
</script>
</head>
<body>

<h2>AJAX</h2>
<button type="button" onclick="loadXMLDoc()">請求數據</button>
<div id="myDiv"></div>
 
</body>
</html>

  

  周末結束了!以上。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 上一篇給大家的三段代碼不知到大家有沒有練習呢?今天再給大家帶來兩段DOM的練習! 4.封裝函數,實現children功能,最好哎原型鏈上編程 ...
  • CPU上電後,會在某個地址開始執行,比如MIPS結構的CPU會從0xBFC00000取第一條指令,而ARM結構的CPU則從0x00000000開始,嵌入式開發板中,需要把存儲器件ROM或Flash等映射到這個地址。而Bootloader就存在這個地址的開始處,這樣一上電後就會從這個地址處執行。Boo ...
  • [TOC] 引言 剛接觸正則表達式,我也曾被它們天書似的符號組合給嚇住,但經過一段時間的深入學習,發現它並沒有想象中那麼可怕,只要多實踐,多理解,也是可以輕鬆搞定的。 而且我發現帶著問題去學習,求知欲會驅使著你往前走,不知不覺就懂了。 下麵就是我在學習中提出的幾個問題,在後面會依次進行討論。由於正則 ...
  • 前言 上篇博客的內容是守護進程,對於操作系統來說可以在後臺執行一些程式.這篇的內容是互斥鎖,在上上篇博客上說到進程記憶體空間互相隔離,所以可以通過共用文件來操作同一個文件,那麼這樣操作的話會發生什麼呢? 鎖 互斥鎖 多個進程需要共用數據時,先將其鎖定,此時資源狀態為'鎖定',其他進程不能更改;知道該進 ...
  • 最近有朋友問我這麼一個面試題目: 現在有一個非常龐大的數據,假設全是 int 類型。現在我給你一個數,你需要告訴我它是否存在其中(儘量高效)。 ...
  • 背景 有個PHPCMS的網站停了,但是網站的歷史文章又想要看看,網站停了以後,管理員發來了網站的所有數據。 分析 因為不會PHP,所有本地環境跑網站不優先考慮。 有MySQL資料庫文件,但是不熟悉MySQL資料庫,也就不下載資料庫了。 有PHPCMS導出的SQL文件,但是不是標準的SQL文件,不能用 ...
  • 本人最近在學習Redis的使用和底層原理,有一些收穫,所以希望通過寫博客的形式來記錄自己的學習過程,加深自己的理解,同時也方便以後查閱複習。目前打算先記錄一些基本的使用方法和部分底層實現,其他的如果有用到我再貼上來。文章內容是我根據自己的理解和參考網上的資料總結的,如果有錯誤的地方,就麻煩各位大佬批 ...
  • 1.while 迴圈 : 2.for 迴圈: ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...