一、python簡單爬取靜態網頁


一、簡單爬蟲框架

  簡單爬蟲框架由四個部分組成:URL管理器、網頁下載器、網頁解析器、調度器,還有應用這一部分,應用主要是NLP配合相關業務。

  它的基本邏輯是這樣的:給定一個要訪問的URL,獲取這個html及內容(也可以獲取head和cookie等其它信息),獲取html中的某一類鏈接,如a標簽的href屬性。從這些鏈接中繼續訪問相應的html頁面,然后獲取這些html的固定標簽的內容,並把這些內容保存下來。

  一些前提:;所有要爬取的頁面,它們的標簽格式都是相同的,可以寫一個網頁解析器去獲取相應的內容;給定的URL(要訪問的資源)所獲得的html,它包含的標簽鏈接是可以篩選的,篩選后的標簽鏈接(新的URL)會被繼續請求其html文檔。調度器是一個循環體,循環處理這些URL、請求以及html、網頁解析。

  1.運行流程

      

    調度器是一個主循環體,負責不斷重復執行URL管理器、下載器、解析器。URL是管理新的URL的添加、舊的URL的去除,以及URL的去重和記錄。下載器顧名思義,就是根據URL,發送http請求,獲取utf-8編碼的字節流的html文件數據。解析器負責將html還原成DOM對象,並提供一套類似js的DOM操作的方法,從html中獲取節點、屬性、文本、甚至是樣式等內容。

  2.URL管理器

    URL管理器有兩個功能,獲取待添加的URL--判斷它是否在已被讀取的URL集合里--[No]判斷它是否在待讀取的URL集合里--[No]添加到待讀取的URL集合里。否則就直接拋棄。

    URL管理器一般放在內存、關系型數據庫和緩存數據庫里。python里可以使用set()集合去重。

  3.網頁下載器

    向給定的URL發送請求,獲取html。python的兩個模塊。內置urllib模塊和第三方模塊request。python3將urllib2封裝成了urllib.request模塊。

 1 # 網頁下載器代碼示例
 2 import urllib
 3 
 4 url = "http://www.baidu.com"
 5 
 6 print("第一種方法: 直接訪問url")
 7 response1 = urllib.request.urlopen(url)
 8 print(response1.getcode())  # 狀態碼
 9 print(len(response1.read()))  # read讀取utf-8編碼的字節流數據
10 
11 print("第二種方法: 設置請求頭,訪問Url")
12 request = urllib.request.Request(url)  # 請求地址
13 request.add_header("user-agent", "mozilla/5.0")  # 修改請求頭
14 response2 = urllib.request.urlopen(request)
15 print(response2.getcode())
16 print(len(response2.read()))
17 
18 import http.cookiejar  # 不知道這是啥
19 
20 print("第三種方法: 設置coockie,返回的cookie")
21 # 第三種方法的目的是為了獲取瀏覽器的cookie內容
22 cj = http.cookiejar.CookieJar()
23 opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj))
24 urllib.request.install_opener(opener)
25 response3 = urllib.request.urlopen(url)
26 print(response3.getcode())
27 print(len(response3.read()))
28 print(cj)  # 查看cookie的內容

  4.網頁解析器

    將utf-8編碼的字節碼重新重新解析為html。因為數據傳輸是字節數據,所以網頁下載器下載的內容需要重新解析。

    提供DOM對象[html文檔解構]的操作方法。和js類似。包括節點、標簽元素、屬性[包括name、class、style、value等等]、樣式、內容等的操作。從而能夠獲取特定的內容。

    python的BeautifulSoup模塊(bs4)。以下代碼可直接在bs4模塊官方文檔中獲取和運行。

 1 from bs4 import BeautifulSoup
 2 from re import compile
 3 html_doc = """
 4 <html><head><title>The Dormouse's story</title></head>
 5 <body>
 6 <p class="title"><b>The Dormouse's story</b></p>
 7 
 8 <p class="story">Once upon a time there were three little sisters; and their names were
 9 <a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
10 <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
11 <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
12 and they lived at the bottom of a well.</p>
13 
14 <p class="story">...</p>
15 """
16 
17 soup = BeautifulSoup(html_doc, "html.parser")
18 print(soup.prettify())
19 print(soup.title)
20 print(soup.title.name)
21 print(soup.title.string)
22 print(soup.title.parent.name)
23 print(soup.p)
24 print(soup.p['class'])
25 print(soup.a)
26 print(soup.find_all(href=compile(r"/example.com/\S*")))
27 print(soup.find_all('a'))
28 print(soup.find(id="link3"))
29 print(soup.get_text())
30 print(soup.find("p", attrs={"class": "story"}).get_text())
31 
32 for link in soup.find_all('a'):
33     print(link.get('href'))

二、簡單示例

  爬取百度百科上詞條為python的以href='/tem/'開頭的所有相關網頁的詞條簡介。

  1 from re import compile
  2 from html.parser import HTMLParser
  3 from bs4 import 
  4 
  5 # url管理器
  6 class UrlManager(object):
  7     """
  8     url管理器主要有三個功能:add_new_url添加新的待爬取的頁面;get_new_url刪除已爬取的頁面;標記待爬取的和已爬取的頁面。
  9     """
 10     def __init__(self):
 11         self.new_urls = set()
 12         self.old_urls = set()
 13     def add_new_url(self, url):
 14         if url is None:
 15             return 
 16         # 如果傳入的url既不在待爬取的url里又不在爬過的url里,說明它是待爬取的url
 17         if url not in self.new_urls and url not in self.old_urls:
 18             self.new_urls.add(url)
 19     
 20     def add_new_urls(self, urls):
 21         if urls is None or len(urls) == 0:
 22             return 
 23         for url in urls:
 24             self.add_new_url(url)
 25             
 26     def has_new_url(self):
 27         return len(self.new_urls) != 0
 28     
 29     def get_new_url(self):
 30         new_url = self.new_urls.pop()  # 從待爬去的url中剔除要爬取的目標
 31         self.old_urls.add(new_url)     # 添加到
 32         return new_url
 33 
 34 # 簡單的下載器
 35 class HtmlDownloader(object):
 36     def download(self, url):
 37         if url is None:
 38             return None
 39         response = urllib.request.urlopen(url)
 40         if response.getcode() != 200:
 41             return None
 42         return response.read()
 43     
 44 # 解析器
 45 class HtmlParser(object):
 46     def _get_new_urls(self, page_url, soup):
 47         # 這里要提一下,百度百科python詞匯的url是https://baike.baidu.com/item/Python/407313
 48         # 頁面中的a標簽的href屬性都類似href="/item/%E6%95%99%E5%AD%A6"這種屬性
 49         # 在處理時,需要加上baike.baidu.com保證url資源定位符的完整性。后面只需匹配"/item/"
 50         new_urls = set()
 51         links = soup.find_all('a', href=compile(r"/item/\S*"))
 52         for link in links:
 53             new_url = link["href"]
 54             new_full_url = urllib.parse.urljoin(page_url, new_url)
 55             new_urls.add(new_full_url)
 56         return new_urls
 57     
 58     def _get_new_data(self, page_url, soup):
 59         res_data = {}
 60         res_data["url"] = page_url
 61         # 爬取標題
 62         # <dd class="lemmaWgt-lemmaTitle-title"></dd><h1>Python</h1>
 63         title_node = soup.find("dd", attrs={"class": "lemmaWgt-lemmaTitle-title"}).find("h1")
 64         res_data["title"] = title_node.get_text()
 65         # 爬取簡介內容
 66         # <div class="lemma-summary" label-module="lemmaSummary"></div>
 67         # 這個div下的所有div里的text
 68         summary_node = soup.find('div', attrs={"class": "lemma-summary", "label-module":"lemmaSummary"})
 69         res_data["summary"] = summary_node.get_text()
 70         return res_data
 71     
 72     def parse(self, page_url, html_doc):
 73         if page_url is None or html_doc is None:
 74             return 
 75         # 解析成了一個整個的DOM對象,也就是純html格式的文件
 76         soup = BeautifulSoup(html_doc, "html.parser", from_encoding="utf-8")
 77         new_urls = self._get_new_urls(page_url, soup)
 78         new_data = self._get_new_data(page_url, soup)
 79         # print("page_url: %r, new_urls: %r, new_data: %r" % (page_url, new_urls, new_data))
 80         return new_urls, new_data
 81 
 82 # 輸出器
 83 class HtmlOutputer(object):
 84     def __init__(self):
 85         self.datas = []
 86     def collect_data(self, data):
 87         if data is None:
 88             return 
 89         self.datas.append(data)
 90     def output_html(self):
 91         fout = open("output.html", 'w', encoding="UTF-8")
 92         fout.write("<html>")
 93         fout.write("<meta http-equiv='content-type' content='text/html;charset=utf-8'>")
 94         fout.write("<body>")
 95         fout.write("<table>")
 96         for data in self.datas:
 97             fout.write("<tr>")
 98             fout.write("<td>%s</td>" %data['url'])
 99             fout.write("<td>%s</td>" %data['title'])
100             fout.write("<td>%s</td>" %data['summary'])
101             fout.write("</tr>")
102         fout.write("</table>")
103         fout.write("</body>")
104         fout.write("</html>")
105 
106 class SpiderMain(object):
107     def __init__(self):
108         self.urls = UrlManager()
109         self.downloader = HtmlDownloader()
110         self.parser = HtmlParser()
111         self.outputer = HtmlOutputer()
112     
113     def craw(self, root_url):
114         count = 1
115         self.urls.add_new_url(root_url)
116         while self.urls.has_new_url():
117             try:
118                 new_url = self.urls.get_new_url()
119                 html_cont = self.downloader.download(new_url)
120                 # print("\033[1;36m %r \033[0m" % html_cont.decode("utf-8"))
121                 new_urls, new_data = self.parser.parse(new_url, html_cont)
122                 self.urls.add_new_urls(new_urls)
123                 self.outputer.collect_data(new_data)
124                 if count == 11:break
125                 print("\033[1;36m [CRAW]\033[0m :  %d %r" %(count, new_url))
126                 count += 1
127             except Exception as e:
128                 print("craw failed")
129                 print(e)
130         self.outputer.output_html()

    運行結果如下:

    打開保存的out.html,內容如下:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM