python 爬蟲開發從入門到實踐 讀書筆記(一)
正則表示式
正則表示式的基本符號
-
點號 "." 代替除了
換行符
以外的任何一個字元 -
星號 "*" 表示前面的一個子表示式(普通字元/另一個或幾個正則表示式符號
0
到無限次
-
問號 "?" 表示前面的子表示式
0
或1
次 -
反斜槓 "" 不單獨使用
- 轉義
- \n 換行符
- \t 製表符
- \ 普通反斜槓
- ' 單引號
- " 雙引號
- \d 數字
- 小括號 () 把括號裡的內容提取出來
使用正則表示式
findall
返回所有
滿足要求的字串
格式
re.findall(正則表示式, 原來的字串, flags=0)
結果是一個列表, 包含了所有匹配的結果.如果沒有匹配到,就是空列表
import re content = "賬號A密碼:12345, 賬號B密碼:67890, 所有密碼" password_list = re.findall(':(.*?),', content) print(password_list) account_password = re.findall("賬號(.*?)密碼:(.*?),", content) print(account_password) ==> ['12345', '67890'] [('A', '12345'), ('B', '67890')]
flags 引數一些輔助功能,例如忽略大小寫,re.S 忽略換行符.
import re html_string = ''' 賬號A密碼是123 456, ''' password_no_flag = re.findall('密碼是(.*?),', html_string) password_flag = re.findall('密碼是(.*?),', html_string, re.S) print(password_no_flag) print(password_flag)
返回第一個
滿足要求的字串
格式
re.search(正則表示式, 原來的字串, flags=0)
結果是一個正則表示式的物件, 匹配不到就是 None. 如果需要得到匹配的結果, 通過 .group() 獲取值
import re content = "賬號A密碼:12345, 賬號B密碼:67890, 所有密碼" password_search = re.search("賬號(.*?)密碼:(.*?),", content) print(password_search) print(password_search.group()) print(password_search.group(0)) print(password_search.group(1)) # 對應上面第一個括號 print(password_search.group(2)) # 對應上面第二個括號
貪婪匹配
獲得最短的能滿足條件的表示式
括號內可以有其他字元, 比如 (20178*?)
python 檔案操作
格式
with open('檔案路徑', '檔案操作方式', encoding='utf-8') as f: 對檔案進行操作
使用 Python 讀檔案
檔案路徑是絕對路徑, 也可以是相對路徑. 但是不能使用 "~"家目錄這種形式, 但是可以轉化
import os real_path = os.path.expanduser('~/project/xxx')
讀檔案
with open('text.txt', encoding='utf-8') as f: content_list = f.readlines() # readlines 按照列表返回 print(content_list) with open('text.txt', encoding='utf-8') as f: content_str = f.read() # read 返回一個整體字串 print(content_str)
使用 Python 寫檔案
with open("text.txt", "w", encoding="utf-8") as f: # w 新生成一個檔案, wa 追加一個檔案 f.write("你好") # 寫一大段字串 f.write("\n") # 寫到文字的檔案不會自動換行 f.writelines(["第一\n", "第二"]) # 寫入一個列表 f.write("\n") f.write("end")
使用 Python 讀/寫csv 檔案
寫 csv 檔案
可以把字典寫成 csv 檔案, 或者把包含字典的列表寫成 csv 檔案, 列名和字典的 key 一一對應
import csv data = [{"name": "user01", "age": 10}, {"name": "user02", "age": 20}, {"name": "user03", "age": 30}] with open("new.csv", "w", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=['name', 'age']) writer.writeheader() # 表頭 writer.writerows(data) # 資料 writer.writerow({"name": "user04", "age": 40})
讀 csv 檔案
第一種方法, 直接讀
with open('new.csv', encoding="utf-8") as f: reader = csv.DictReader(f) for row in reader: print(row) # 這是個字典
第二種發放,利用列表推導式
with open('new.csv', encoding="utf-8") as f: reader = [x for x in csv.DictReader(f)] for row in reader: print(row)
Python 獲取原始碼
requests
GET
import requests html = requests.get("https://baidu.com").content.decode() print(html)
POST
import requests data = {'k1': 'v1', 'k2': 'v2'} html_formdata = requsts.post("url", data=data).content.decode() html_formdata = requsts.post("url", json=data).content.decode() # requests 自動將字典轉 json
結合正則
title = re.search('title>(.*?)<', html, re.S).group(1) # 提取標題 content_list = re.findall("p>(.*?)<", html, re.S) # 提取正文 content_str = '\n'.join(content_list) # 用換行符拼起來
多執行緒爬蟲
多執行緒
from multiprocessing.dummy import Pool def calc_power2(num): return num*num pool = Pool(3) # Pool 實現執行緒池 origin_num = [x for x in range(10)] result = pool.map(calc_power2, origin_num) print(f"{result}") => [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
小說爬蟲例子
import requests import re import os from multiprocessing.dummy import Pool url_start = "http://www.kanunu8.com/book3/6879/" html_start = requests.get(url_start).content.decode("GBK") toc_block = re.findall("正文(.*?)</tbody>", html_start, re.S)[0] toc_url = re.findall('href="(.*?)"', toc_block, re.S) toc_url_list = [] for url in toc_url: toc_url_list.append(url_start + url) print(toc_url_list) os.makedirs("動物農場", exist_ok=True) def get_doc_content(url): print(url) chapter_html = requests.get(url).content.decode("GBK") chapter_name = re.search('size="4">(.*?)<', chapter_html, re.S).group(1) text_block = re.search('<p>(.*?)</p>', chapter_html, re.S).group(1) text_block = text_block.replace('<br />', '') with open(os.path.join("動物農場", chapter_name + '.txt'), 'w', encoding="utf-8") as f: f.write(text_block) pool = Pool(8) result = pool.map(get_doc_content, toc_url_list)
html 內容解析
pip install lxml
返回一個列表
語法
- 獲取文字 //標籤1[@屬性1="屬性值1"]/標籤2[@屬性2="屬性值2"]/.../text()
- 獲取屬性值 //標籤1[@屬性1="屬性值1"]/標籤2[@屬性2="屬性值2"]/.../@屬性n# a/@href
- 以相同的字串開頭 //標籤[starts-with(@屬性名, "相同的開頭部分")] # //div[starts-with(@id, "test")]/text()
- 屬性值包含相同字串 //標籤[starts-with(@屬性名, "包含的字串")]
- 對 xpath 返回的物件再次執行 xpath , 子 xpath 開頭不需要新增斜線, 直接標籤
- 使用 string(.) 提取文字
import lxml.html selector = lxml.html.fromstring(html) info = selector.xpath('xpath 語句') info_list = info[0].xpath(ul/li/text()) info_string = info.xpath('string(.)')
Beautifull Soup4
pip install beautifullsoup4 from bs4 import BeautifullSoup
解析原始碼
soup = BeautifulSoup("網頁原始碼", "解析器") soup = BeautifulSoup("網頁原始碼", 'html.parser') # 兩種解析器 soup = BeautifulSoup("網頁原始碼", "lxml") from bs4 import BeautifullSoup info = soup.find(class_="test") all_content = info.find_all('li') for li in all_content: print(li.string) # 通過 string 讀出文字資訊
find() 與 find_all() 引數相同
find(name, attrs, recursive, text, **kwargs)
- name html 的標籤名, body, div, ul, li
- attrs 一個字典, key 是屬性名, value是屬性值 attrs={'class': 'usefull'}
- recursive 的值為 True 或 False. 當為 False , BS4 不會搜尋子標籤
- text 可以是一個字串或正則表示式, 使用者搜尋標籤裡的文字資訊
content = soup.find_all(text=re.compile('文字資訊')) for each in content: print(each.string)
- **kwarg 表示 k=v 形式的引數. k 是屬性, v 是屬性值. 有時候屬性非常特殊, 可以省略標籤
find_all('div', id='test') find_all(class_='iamstrange') content = soup.find_all(re.compile('iam')) # 支援正則匹配 for each in content: print(each.string)