網頁結構、使用urlopen()爬取網頁內容、常用正則表達式簡介、使用正則表達式匹配抓取網頁內容、使用BeautifulSoup匹配抓取網頁內容

網頁的結構:

我們通過一個最簡單的網頁來分析網頁的結構。

https://morvanzhou.github.io/static/scraping/basic-structure.html

<!DOCTYPE html>
<html lang="cn">
<head>
	<meta charset="UTF-8">
	<title>Scraping tutorial 1 | 莫煩Python</title>
	<link rel="icon" href="https://morvanzhou.github.io/static/img/description/tab_icon.png">
</head>
<body>
	<h1>爬蟲測試1</h1>
	<p>
		這是一個在 <a href="https://morvanzhou.github.io/">莫煩Python</a>
		<a href="https://morvanzhou.github.io/tutorials/data-manipulation/scraping/">爬蟲教程</a> 中的簡單測試.
	</p>

</body>
</html>

HTML網頁中上所有的實體內容, 都會有個 tag 來框住它。網頁的主體的tag分成兩部分, 即header 和 body。

header 中, 存放着一些網頁的元信息, 比如title最終是顯示在網頁標籤上的。

如:

	<title>Scraping tutorial 1 | 莫煩Python</title>

body存放的是網頁的主體信息。如heading, 視頻, 圖片和文字等。

<h1></h1> tag是主標題, 我們看到呈現出來的效果就是大一號的文字。

如:

<h1>爬蟲測試1</h1>

<p></p> 裏面的文字就是一個段落。<a></a>裏面都是鏈接。

如:

這是一個在 <a href="https://morvanzhou.github.io/">莫煩Python</a>
<a href="https://morvanzhou.github.io/tutorials/data-manipulation/scraping/">爬蟲教程</a> 中的簡單測試.

使用urlopen()爬取網頁內容:

如:

from urllib.request import urlopen

html = urlopen(
	"https://morvanzhou.github.io/static/scraping/basic-structure.html"
).read().decode('utf-8')
# 如果網頁中含有中文,則decode()參數爲'utf-8
print(html)

運行結果如下:

<!DOCTYPE html>
<html lang="cn">
<head>
	<meta charset="UTF-8">
	<title>Scraping tutorial 1 | 莫煩Python</title>
	<link rel="icon" href="https://morvanzhou.github.io/static/img/description/tab_icon.png">
</head>
<body>
	<h1>爬蟲測試1</h1>
	<p>
		這是一個在 <a href="https://morvanzhou.github.io/">莫煩Python</a>
		<a href="https://morvanzhou.github.io/tutorials/data-manipulation/scraping/">爬蟲教程</a> 中的簡單測試.
	</p>

</body>
</html>

可以看到我們將網頁的源代碼下載了下來。然後我們要將下載下來的源代碼作爲參數提供給BeautifulSoup,讓BeautifulSoup來做匹配,找出其中我們需要的信息。

常用正則表達式簡介:

^匹配字符串的開頭。

$匹配字符串的末尾。

/順斜槓是表示表達式開始和結束的“定界符”。

\反斜槓是表示轉義字符。

.是匹配任意字符的意思。

“+”表示前面表達式匹配一次至多次(比如.+即匹配任意字符一次或多次)。

*表示匹配前一個字符0次或無限次(貪婪匹配)。

?表示匹配前一個字符0次或1次(非貪婪匹配,最小匹配)。

(.+)默認是貪婪匹配。.+?表示匹配任意字符並最小匹配。(.+?)加上括號後表示匹配到的對象設置成多個分組(如果匹配到了多個對象)。

如:

<a href="xxx"><span>
如果用<.+>匹配,則匹配結果是
<a href="xxx"><span>
如果用<.+?>匹配,則匹配結果是
<a href="xxx">

.* 就是單個字符匹配任意次,即貪婪匹配。.*? 表示匹配滿足前後條件時只匹配最小範圍的情況,即最小匹配。

如:

<H1>Chapter 1 - 介紹正則表達式</H1>
使用/<.*>/匹配的結果爲:
H1>Chapter 1 - 介紹正則表達式</H1
使用/<.*?>/匹配結果爲:
H1

使用正則表達式匹配抓取網頁內容:

實際上,如果是初級的匹配,只要使用正則表達式來匹配就可以了。我們先看看用正則表達式如何匹配。

正則表達式匹配抓取標籤內容:

from urllib.request import urlopen
import re

html = urlopen(
	"https://morvanzhou.github.io/static/scraping/basic-structure.html"
).read().decode('utf-8')
# 如果網頁中含有中文,則decode()參數爲'utf-8
res = re.findall(r"<title>(.+?)</title>", html)
# 上面下載下來的網頁內容html作爲匹配源
print("\n 本網頁的title是", res)
print("\n 本網頁的title是", res[0])

運行結果如下:

 本網頁的title是 ['Scraping tutorial 1 | 莫煩Python']

 本網頁的title是 Scraping tutorial 1 | 莫煩Python

正則表達式匹配抓取段落內容:

如果想要找到中間的那個段落 <p>, 因爲段落在 HTML 中還夾雜着 tab, new line, 我們給一個flags=re.DOTALL來對這些 tab, new line不敏感。

from urllib.request import urlopen
import re

html = urlopen(
	"https://morvanzhou.github.io/static/scraping/basic-structure.html"
).read().decode('utf-8')
# 如果網頁中含有中文,則decode()參數爲'utf-8
res = re.findall(r"<p>(.*?)</p>", html, flags=re.DOTALL)
# 上面下載下來的網頁內容html作爲匹配源
print("\n 本網頁的段落是", res)
print("\n 本網頁的段落是", res[0])

運行結果如下:

 本網頁的段落是 ['\n\t\t這是一個在 <a href="https://morvanzhou.github.io/">莫煩Python</a>\n\t\t<a href="https://morvanzhou.github.io/tutorials/data-manipulation/scraping/">爬蟲教程</a> 中的簡單測試.\n\t']

 本網頁的段落是 
		這是一個在 <a href="https://morvanzhou.github.io/">莫煩Python</a>
		<a href="https://morvanzhou.github.io/tutorials/data-manipulation/scraping/">爬蟲教程</a> 中的簡單測試.

正則表達式匹配抓取鏈接:

from urllib.request import urlopen
import re

html = urlopen(
	"https://morvanzhou.github.io/static/scraping/basic-structure.html"
).read().decode('utf-8')
# 如果網頁中含有中文,則decode()參數爲'utf-8
res = re.findall(r'href="(.*?)"', html)
# 上面下載下來的網頁內容html作爲匹配源
print("\n 本網頁的title是", res)
print("\n 本網頁的title是", res[0])

運行結果如下:

本網頁的鏈接是 ['https://morvanzhou.github.io/static/img/description/tab_icon.png', 'https://morvanzhou.github.io/', 'https://morvanzhou.github.io/tutorials/data-manipulation/scraping/']

 本網頁的鏈接是 https://morvanzhou.github.io/static/img/description/tab_icon.png

使用BeautiSoup匹配抓取網頁內容:

BeautifulSoup的安裝方法:

python3 -m pip install beautifulsoup4

BeautifulSoup匹配抓取網頁內容的步驟:

選擇要爬取的網址 (url);

使用urlopen等登錄這個網址,read() 讀取網頁信息;

將讀取的信息放入BeautifulSoup,使用 BeautifulSoup(代替正則表達式)匹配你需要抓取的信息。

BeautifulSoup解析基礎網頁:

from bs4 import BeautifulSoup
from urllib.request import urlopen

html = urlopen("https://morvanzhou.github.io/static/scraping/basic-structure.html").read().decode('utf-8')
soup = BeautifulSoup(html, features='lxml')
# 現在用BeautifulSoup代替正則表達式匹配來找出title和段落等部分,features='lxml'即選擇lxml爲解析器
print(soup.h1)
# 返回title
print('\n', soup.p)
# 返回段落
all_href = soup.find_all('a')
# 找到所有a標籤
print(all_href)
# 這時候我們找到的是包含<a,</a>的鏈接
all_href = [l['href'] for l in all_href]
# 生成器寫法
# 上面這句等於:
# for l in all_href:
#  print(l['href'])
# 上面相當於打印出字典中key=href對應的value值即鏈接
print(all_href)
# 這時候找到的就只是鏈接了,去掉了頭尾的<a,</a>等

運行結果如下:

<h1>爬蟲測試1</h1>

 <p>
		這是一個在 <a href="https://morvanzhou.github.io/">莫煩Python</a>
<a href="https://morvanzhou.github.io/tutorials/data-manipulation/scraping/">爬蟲教程</a> 中的簡單測試.
	</p>
[<a href="https://morvanzhou.github.io/">莫煩Python</a>, <a href="https://morvanzhou.github.io/tutorials/data-manipulation/scraping/">爬蟲教程</a>]
['https://morvanzhou.github.io/', 'https://morvanzhou.github.io/tutorials/data-manipulation/scraping/']

BeautifulSoup解析CSS網頁:

CSS可以起到分別裝飾每一個網頁部件的作用。在裝飾每一個網頁部件的時候, 都會給它一個名字。同一種類型的部件, 名字都可以一樣。部件裏面的字體/背景顏色, 字體大小, 都是由 CSS 來控制。

先看一個使用CSS的網頁結構:

<!DOCTYPE html>
<html lang="cn">
<head>
	<meta charset="UTF-8">
	<title>爬蟲練習 列表 class | 莫煩 Python</title>
	<style>
	.jan {
		background-color: yellow;
	}
	.feb {
		font-size: 25px;
	}
	.month {
		color: red;
	}
	</style>
</head>

<body>

<h1>列表 爬蟲練習</h1>

<p>這是一個在 <a href="https://morvanzhou.github.io/" >莫煩 Python</a> 的 <a href="https://morvanzhou.github.io/tutorials/data-manipulation/scraping/" >爬蟲教程</a>
	裏無敵簡單的網頁, 所有的 code 讓你一目瞭然, 清晰無比.</p>

<ul>
	<li class="month">一月</li>
	<ul class="jan">
		<li>一月一號</li>
		<li>一月二號</li>
		<li>一月三號</li>
	</ul>
	<li class="feb month">二月</li>
	<li class="month">三月</li>
	<li class="month">四月</li>
	<li class="month">五月</li>
</ul>

</body>
</html>

如上例,<head></head>中的<style></style>這段代碼就是我們的CSS代碼,這裏由於比較簡單直接寫在了<head></head>中,正常情況下這段代碼會單獨保存成一個CSS文件。

在<ul></ul>中我們可以看到元素是以class來分類保存的。

我們可以用BeautifulSoup通過CSS的class分類來抓取我們需要的信息。

from bs4 import BeautifulSoup
from urllib.request import urlopen

html = urlopen("https://morvanzhou.github.io/static/scraping/list.html").read().decode('utf-8')
soup = BeautifulSoup(html, features='lxml')
# 現在用BeautifulSoup代替正則表達式匹配來找出title和段落等部分,features='lxml'即選擇lxml爲解析器
month = soup.find_all('li', {'class': 'month'})
# 如果只傳入'li'則所有'li'都會被選中,加上{'class':'month'}後所有li中的month類纔會被選中
for m in month:
	print(m.get_text())
	print(m)
# 上面兩句print的區別是前一句只會打出<li></li>之間的內容,而後一句會打出包括<li></li>的內容
jan = soup.find('ul', {'class': 'jan'})
d_jan = jan.find_all('li')
for d in d_jan:
	print(d.get_text())
# 打印出jan中的所有日期

運行結果如下:

一月
<li class="month">一月</li>
二月
<li class="feb month">二月</li>
三月
<li class="month">三月</li>
四月
<li class="month">四月</li>
五月
<li class="month">五月</li>
一月一號
一月二號
一月三號

BeautifulSoup搭配正則表達式匹配解析網頁:

from bs4 import BeautifulSoup
from urllib.request import urlopen
import re

html = urlopen("https://morvanzhou.github.io/static/scraping/table.html").read().decode('utf-8')
soup = BeautifulSoup(html, features='lxml')
# 現在用BeautifulSoup代替正則表達式匹配來找出title和段落等部分,features='lxml'即選擇lxml爲解析器
# 我們現在想找出網頁中的圖片鏈接
img_links = soup.find_all("img", {"src": re.compile('.*?\.jpg')})
# re.compile函數用於編譯正則表達式,生成一個正則表達式(Pattern)對象。
for link in img_links:
	print(link['src'])
# 找到不是圖片的其他鏈接
course_links = soup.find_all('a', {'href': re.compile('https://morvan.*')})
for link in course_links:
	print(link['href'])

運行結果如下:

from bs4 import BeautifulSoup
from urllib.request import urlopen
import re

html = urlopen("https://morvanzhou.github.io/static/scraping/table.html").read().decode('utf-8')
soup = BeautifulSoup(html, features='lxml')
# 現在用BeautifulSoup代替正則表達式匹配來找出title和段落等部分,features='lxml'即選擇lxml爲解析器
# 我們現在想找出網頁中的圖片鏈接
img_links = soup.find_all("img", {"src": re.compile('.*?\.jpg')})
# re.compile函數用於編譯正則表達式,生成一個正則表達式(Pattern)對象。
for link in img_links:
	print(link['src'])
# 找到不是圖片的其他鏈接
course_links = soup.find_all('a', {'href': re.compile('https://morvan.*')})
for link in course_links:
	print(link['href'])

練習:爬取百度百科

from bs4 import BeautifulSoup
from urllib.request import urlopen
import re
import random
import time

base_url = "https://baike.baidu.com"
his = ["/item/%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB/5162711"]
for i in range(20):
	# 循環爬20次
	time.sleep(2)
	# 每爬一次歇兩秒,避免百度檢測到你的爬蟲
	url = base_url + his[-1]
	# 這裏url分成了兩部分,his[-1]表示his列表中的最後一項
	html = urlopen(url).read().decode('utf-8')
	soup = BeautifulSoup(html, features='lxml')
	print(soup.find('h1').get_text(), '    url:  ', his[-1])
	# 該網站網絡爬蟲四個字是h1標籤,並打印其鏈接
	# 我們再找這個網頁中所有百度百科的鏈接
	sub_urls = soup.find_all("a", {"target": "_blank", "href": re.compile("/item/(%.{2})+$")})
	# sub_urls獲得該網頁所有其他百度百科的鏈接,_blank放在一個鏈接中表示瀏覽器另外打開一個新窗口,(%.{2})+這樣設置正則表達式,我們找到的網址就都是 /item/%xx%xx%xx...這樣的格式了。
	if len(sub_urls) != 0:
		his.append(random.sample(sub_urls, 1)[0]['href'])
	# 從sub_urls list中隨機獲取1個元素,作爲一個片斷返回,添加到his中
	else:
		his.pop()

運行結果如下:

網絡爬蟲     url:   /item/%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB/5162711
寬度優先搜索     url:   /item/%E5%B9%BF%E5%BA%A6%E4%BC%98%E5%85%88%E6%90%9C%E7%B4%A2
隊列     url:   /item/%E9%98%9F%E5%88%97
數組     url:   /item/%E6%95%B0%E7%BB%84
字節     url:   /item/%E5%AD%97%E8%8A%82
亂碼     url:   /item/%E4%B9%B1%E7%A0%81
電子郵件程序     url:   /item/%E7%94%B5%E5%AD%90%E9%82%AE%E4%BB%B6%E7%A8%8B%E5%BA%8F
點擊     url:   /item/%E7%82%B9%E5%87%BB
網絡營銷     url:   /item/%E7%BD%91%E7%BB%9C%E8%90%A5%E9%94%80
百度百科:多義詞     url:   /item/%E7%99%BE%E5%BA%A6%E7%99%BE%E7%A7%91%EF%BC%9A%E5%A4%9A%E4%B9%89%E8%AF%8D
趙氏孤兒大報仇     url:   /item/%E8%B5%B5%E6%B0%8F%E5%AD%A4%E5%84%BF
義項     url:   /item/%E4%B9%89%E9%A1%B9
趙氏孤兒大報仇     url:   /item/%E8%B5%B5%E6%B0%8F%E5%AD%A4%E5%84%BF
百度百科:多義詞     url:   /item/%E7%99%BE%E5%BA%A6%E7%99%BE%E7%A7%91%EF%BC%9A%E5%A4%9A%E4%B9%89%E8%AF%8D
趙氏孤兒大報仇     url:   /item/%E8%B5%B5%E6%B0%8F%E5%AD%A4%E5%84%BF
義項     url:   /item/%E4%B9%89%E9%A1%B9
約翰內斯·勃拉姆斯     url:   /item/%E7%BA%A6%E7%BF%B0%E5%A5%88%E6%96%AF%C2%B7%E5%8B%83%E6%8B%89%E5%A7%86%E6%96%AF
約瑟夫·約阿希姆     url:   /item/%E7%BA%A6%E7%91%9F%E5%A4%AB%C2%B7%E7%BA%A6%E9%98%BF%E5%B8%8C%E5%A7%86
種族歧視     url:   /item/%E7%A7%8D%E6%97%8F%E6%AD%A7%E8%A7%86
美國最高法院     url:   /item/%E7%BE%8E%E5%9B%BD%E6%9C%80%E9%AB%98%E6%B3%95%E9%99%A2

Process finished with exit code 0

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章