Python爬蟲系統化學習(3)

通常來講當咱們爬取網頁的整個源代碼後,是須要對網頁進行解析的。css

正常的解析方法有三種html

①:正則匹配解析python

②:BeatuifulSoup解析正則表達式

③:lxml解析函數


正則匹配解析:

在以前的學習中,咱們學習過爬蟲的基本用法,好比/s,/d,/w,*,+,?等用法,可是在對爬取到的網頁進行解析的時候,僅僅會這些基礎的用法,是不夠用的,所以咱們須要瞭解Python中正則匹配的經典函數。工具

re.match:

runoob解釋:re.match嘗試從字符串的起始位置匹配一個模式,若是不是起始位置匹配成功的話,match就會返回none,若是匹配成功,re.match就會返回一個匹配的對象,不然也爲null.查看匹配返回的對象要使用group函數的方法。post

語法:學習

re.match(pattern,string,flags=0)

解釋:pattern是匹配的正則表達式,string是所要匹配的字符,而flags是標誌位,用於匹配區分是否大小寫,多行匹配等。具體用法以下列表格:網站

修飾符 描述
re.I 使得匹配對大小寫不敏感
re.M 多行匹配,影響^和$
re.S 使得.匹配包括換行在內的全部字符(正常狀況下.不匹配換行符,這個行爲很危險,容易被黑客利用繞過)
re.U 根據Unicode字符集解析字符,這個標誌影響\w,\W,\b,\B

代碼案例:ui

#!/usr/bin/python
#coding:utf-8
import re
line="Anyone who dreams there gets"
match=re.match(r'(.*) dreams (.*?).*',line,re.M|re.I)
#match1=re.match(r'(.*) Dreams (.*?)',line,re.M)#大小寫敏感,匹配會失敗
match2=re.match(r'(.*) Dreams (.*?).*',line,re.M|re.I)#非貪婪模式
match3=re.match(r'(.*) Dreams (.*).*',line, re.M|re.I)#貪婪模式
print(match.group())
#print(match1.group())
print(match2.group())
print("match 0:",match.group(0))#Anyone who dreams there gets
print("match 1:",match.group(1))#Anyone who
print("match 2:",match.group(2))#' '
print("match2 0:",match2.group(0))#Anyone who dreams there gets
print("match2 1:",match2.group(1))#Anyone who
print("match2 2:",match2.group(2))#' '
print("match3 0:",match3.group(0))#Anyone who dreams there gets
print("match3 1:",match3.group(1))#Anyone who
print("match3 2:",match3.group(2))#there gets

能夠看到(.*?)非貪婪模式會匹配空格,而且放入group(2),默認group()==group(0)位匹配的整句話,而groups()函數會把匹配的結果放到一個列表裏。對匹配返回的結果好比match,咱們可使用start(),end(),span(),來查看開始,結束的位置。

正則匹配字符串r'(.*)'的r表明raw string,表明匹配純粹的字符串,使用它就不會對反引號裏的反斜槓進行特殊處理,好比換行符\n不會進行轉義。所以咱們能夠了解下正則匹配中的轉義:

在不加r的前提下'\\\\'會被轉義,這是由於字符串通過了兩次轉移,一次是字符串轉義,轉義成爲了'\\',而後進行了正則轉義。

若是加了r就會變成'\\',由於以純粹的字符串進行匹配取消了字符串轉義。這就是正則匹配中的轉義流程。


re.search

re.search與re.match不一樣之處在於re.search會掃描整個字符串,而且返回第一個結果,此外re.match並沒有太大差異,一樣可使用m.span(),m.start(),m.end()來查看起始位置等等

re.findall

在以前的一篇博文:https://www.cnblogs.com/Tianwenfeigong/p/14397771.html

使用到過re.findall函數,用以對bilibili中上傳Python教程的Up主進行爬取,從那篇博文中,咱們能夠看出,re.findall返回的是列表,,對他的數據提取通常先用set()轉換爲集合的方式會更佳。

在那篇博文中,我還不懂對href進行包含但不選擇,所以正則匹配的內容有限,致使正則匹配適用的環境並很少,所以從新爬取新的內容,而且引入一個新的小技巧;

目標:爬取第一個bilibili搜索欄全部的python視頻的標題

代碼:

#!/usr/bin/python
#coding=utf-8
import requests
import re
link="https://search.bilibili.com/all?keyword=python&from_source=nav_search&spm_id_from=333.851.b_696e7465726e6174696f6e616c486561646572.9"
headers={"User-Agent":'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:85.0) Gecko/20100101 Firefox/85.0'}
r=requests.get(link,headers=headers)
text=r.text
title_list=re.findall('<a title=(.*?) href=.*?>',text)
for i in title_list:
    print(i)

重點內容:title_list=re.findall('<a title=(.*?) href=.*?>',text)

第二個.?表示匹配知足條件的結果,第一個(.?)表示對知足條件的結果進行提取

經過這個方式咱們就能夠提取標題了

BeautifulSoup解析

BeautifulSoup能夠從HTML或者XML中提取數據,相似一個工具箱,使用BeautifulSoup時,可使用一些解析器使得速度快一點呢,目前比較好的的Lxml HTML解析器,使用方法以下:

soup=BeautifulSoup(r.text,"lxml")

BeautifulSoup的本質是複雜的樹形結構,對他的對象提取通常有三種方法:

①:遍歷文檔樹,遍歷文檔樹相似於Linux的文件目錄,既然是樹形結構必定是從上到下的因此第一個節點必定是header,具體方法以下:

#如要得到以<h1>開頭的標籤,則可使用
soup.header.h1
#會返回相似於<h1 id="name">CuLin</h1>的內容
#若是要得到div的全部子節點,則可使用
soup.header.div.contents
#若是要得到div的全部孩子節點,則可使用
soup.header.div.children
#若是要得到div的子子孫孫節點,則可使用
soup.header.div.descendants
#若是要得到節點a的父節點,則可使用
soup.header.div.a.parents

②:搜索文檔樹:

遍歷文檔樹的侷限性很大,通常不是很推薦使用,更多的仍是搜索文檔樹。使用方法很便捷,精髓在find和find_all函數,好比咱們要爬取博客文章的標題:

#!/usr/bin/python
#coding:utf-8
import requestss
from bs4 import BeautifulSoup
link="http://www.santostang.com/"
r=requests.get(link)
soup=BeautifulSoup(r.text,"lxml")
#打印第一篇標題
first_title=soup.find("h1",class_="post-title").a.text.strip()
#打印全部文章的
all_title=soup.find_all("h1",class_="post-title")
for i in range(len(all_titile)):
    print("No ",i," is ",all_title[i].a.text.strip())

③: CSS選擇器:

Css能夠做爲遍歷文檔樹鼩提取數據,也能夠按照搜索文檔樹的方法去搜索,可是我我的以爲css選擇器不如selenium好用,而且使用上很不方便,因此再也不贅述

此外lxml解析器重點是xpath,這在selenium也有很好的體現,而xpath的選擇咱們能夠在網頁檢查中直接檢查xpath更爲方便。

項目實戰

爬取貝殼網站南京房源信息,總房價,平均房價,地點

#coding:utf-8
import requests
import re
from bs4 import BeautifulSoup
link="https://nj.ke.com/ershoufang/pg"
headers={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:86.0) Gecko/20100101 Firefox/86.0","Host":
"nj.ke.com"}
for i in range(1,20):
    link_=link+str(i)+'/'
    r=requests.get(link_,headers=headers)
    soup=BeautifulSoup(r.text,"html.parser")
    houst_list=soup.find_all('li',class_='clear')
    for house in houst_list:
        name=house.find('div',class_='title').a.text.strip()+'\n'
        houst_info=house.find('div',class_='houseInfo').text.strip()
        houst_info=re.sub('\s+','',houst_info)+'\n'
        total_money=str(house.find('div',class_='totalPrice').span.text.strip())+''
        aver_money=str(house.find('div',class_='unitPrice').span.text.strip())
        adderess=house.find('div',class_='positionInfo').a.text.strip()
        text_="標題:"+name+"房屋信息:"+houst_info+"總價:"+total_money+' '+aver_money+adderess
        with open ('haha.txt',"a+") as f:
            f.write(text_)

結果返回一個haha.txt的文本,內容以下:

相關文章
相關標籤/搜索