正則、xpath、bs4的使用和語法

單字符匹配

. 除換行符之外的任意字符
\d 表示數(shù)字
\D 匹配非數(shù)字
\w 匹配單詞字符[a-z,A-Z,0-9]
\W 匹配非單詞字符
\s 匹配空白字符,空格,\n \t…
\S 匹配非空白字符
^ 匹配以…開頭
$ 匹配以…結(jié)尾
[0-9] => \d 匹配0-9

多字符匹配(貪婪匹配)

  • 匹配*前面的字符任意次數(shù)
  • 匹配+前面的字符至少一次
    ?匹配?前面的字符0-1次
    {n,m}匹配{n,m}前面的字符n-m次
    多字符匹配(非貪婪匹配)
    *?
    +?
    ??
    其他
    ()分組
    |邏輯或
    \轉(zhuǎn)義字符
    re模塊下的方法
    re.compile():構(gòu)建正則表達(dá)式對象
    re.match():從起始位開始匹配,單次匹配,如果匹配到結(jié)果立即返回,反之,返回None
    re.search():在整個字符串中進(jìn)行匹配,單次匹配,如果匹配到結(jié)果立即返回,反之,返回None
    re.findall():匹配出整個字符串中,所有符合正則規(guī)則的結(jié)果,返回一個列表
    re.finditer():匹配出整個字符串中,所有符合正則規(guī)則的結(jié)果,返回的是一個可迭代對象
    re.sub():根據(jù)正則表達(dá)式進(jìn)行字符串替換
    re.split():根據(jù)正則表達(dá)式進(jìn)行分割

正則的用法

def get_rank_data(url='http://top.hengyan.com/dianji/default.aspx?p=1'):
    #構(gòu)建請求頭
    headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36',
    }
    # url, \目標(biāo)url
    # data=None, \默認(rèn)為None表示是get請求,如果不為None說明是get請求
    # timeout 設(shè)置請求的超時時間
    # cafile=None, capath=None, cadefault=False,:證書相關(guān)參數(shù)
    # context=None :忽略證書認(rèn)證
    #urlopen不能添加請求頭
    # response = request.urlopen(url=url,timeout=10)

    #添加請求頭
    req = request.Request(url=url,headers=headers)
    response = request.urlopen(req,timeout=10)

    #響應(yīng)狀態(tài)碼
    code = response.status
    #當(dāng)前請求的url地址
    url = response.url
    print(code,url)

    b_content = response.read()
    # bytes -> str: decode
    # str -> bytes: encode
    # print(b_content)
    html = b_content.decode('utf-8')
    # print(html)
    # #文件操作
    # """
    # w:    w+:    wb:    wb+    a:    a+:    ab:    ab+:    r:    rb:
    # """
    # with open('hengyan.html','w') as file:
    #     file.write(html)

    #證據(jù)正則表達(dá)式解析數(shù)據(jù)
    # re.S 修飾:表示.可以匹配換行符

    pattern = re.compile('<div\sclass="list">(.*?)</div>',re.S)
    ul_str = re.findall(pattern,html)[0]

    pattern1 = re.compile('<ul.*?>(.*?)</ul>',re.S)
    li_strs = re.findall(pattern1,ul_str)[1:]

    for li_str in li_strs:
        # print(li_str)
        pattern = re.compile(
            '<li\sclass="num">(.*?)</li>'+
            '.*?<a.*?>(.*?)</a>'+
            '.*?<li.*?>(.*?)</li>'+
            '.*?<li.*?>(.*?)</li>'+
            '.*?<li.*?>(.*?)</li>'+
            '.*?<li.*?>(.*?)</li>',
            re.S
        )

        data = re.findall(pattern=pattern,string=li_str)[0]
        print(data)

    #提取下一頁:
    if '下一頁' in html:
        #說明還存在下一頁
        pattern = re.compile('<span\sclass="pageBarCurrentStyle">(.*?)</span>',re.S)
        current_page = int(re.findall(pattern,html)[0])
        next_page = current_page+1
        #構(gòu)造下一頁的URL地址
        next_page_url = re.sub('\d+',str(next_page),url)
        print(next_page_url)
        get_rank_data(next_page_url)
    else:
        print('數(shù)據(jù)提取完畢')

if __name__ == '__main__':
    get_rank_data()

xpath

安裝:pip install lxml
引用:from lxml import etree
創(chuàng)建etree對象進(jìn)行指定數(shù)據(jù)解析
1.本地
etree = etree.parse(‘本地路徑’)
etree.xpath(‘xpath表達(dá)式’)
2.網(wǎng)絡(luò)
etree = etree.HTML(‘網(wǎng)絡(luò)請求到頁面的數(shù)據(jù)’)
etree.xpath(‘xpath表達(dá)式’)
常用的xpath表達(dá)式:
1.屬性定位:
找到class屬性值為song的div標(biāo)簽
//div[@class=‘song’]
2.層級索引定位
找到class屬性值為tang的div的直系子標(biāo)簽ul下的第二個子標(biāo)簽li下的直系子標(biāo)簽a
//div[@class=‘tang’]/ul/li[2]/a
3.邏輯運(yùn)算
找到href屬性值為空且class屬性值為du的a標(biāo)簽
//a[@href=’’ and @class=‘du’]
4.模糊匹配
/表示獲取某個標(biāo)簽下的文本內(nèi)容 //div[@class=‘song’]/p[1]/text()
//表示獲取某個標(biāo)簽下的文本內(nèi)容和所有子標(biāo)簽下的文本內(nèi)容 //div[@class=‘tang’]//text()
5.取屬性
//div[@class=‘tang’]//li[2]/a/@href

class HengYanSpider(object):

    def __init__(self):
        self.first_url = 'http://all.hengyan.com/1/0_0_0_0_0_0_0_0_0_1.aspx'
        self.default_headers = {
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
        }

    def get_noval_url(self, url=None):
        url = self.first_url if not url else url
        """獲取小說詳情的url地址"""
        html = self.send_request(url)
        if html:
            # 解析數(shù)據(jù)(獲取xpath解析器)
            etree_html = etree.HTML(html)
            noval_urls = etree_html.xpath('//li[@class="bookname"]/a[1]/@href')
            for noval_url in noval_urls:
                self.get_noval_detail(noval_url)

            # 獲取下一頁
            if '下一頁' in html:
                # 說明還存在下一頁
                current_page = int(self.extract_first(etree_html.xpath('//span[@class="pageBarCurrentStyle"]/text()]')))
                next_page = current_page + 1
                next_page_url = re.sub('\d+.aspx', str(next_page)+'.aspx', url)
                self.get_noval_url(next_page_url)
            else:
                print('數(shù)據(jù)提取完畢')
        else:
            print('數(shù)據(jù)獲取失敗')

    def get_noval_detail(self, noval_url):
        """獲取書籍詳情的頁面內(nèi)容,解析數(shù)據(jù)"""
        html = self.send_request(noval_url)
        if html:
            # 解析數(shù)據(jù)(獲取xpath解析器)
            etree_html = etree.HTML(html)
            # print('得到了詳情頁面')
            noval_dict = {}
            # 書號
            book_id = self.extract_first(etree_html.xpath('//div[@class="dh"]/p/label/text()'))
            noval_dict['book_id'] = re.search('\d+', book_id).group()
            # 熱度
            noval_dict['hot'] = self.extract_first(etree_html.xpath('//p[@class="wendu"]/b/text()'))
            # 火車票
            noval_dict['hot_track'] = self.extract_first(
                etree_html.xpath('//div[@class="piao"]/p[2]/span[@class="huocolor"]/text()'))
            # 冰票
            noval_dict['bing_track'] = self.extract_first(
                etree_html.xpath('//div[@class="piao"]/p[2]/span[@class="bingcolor"]/text()'))
            # 金筆
            noval_dict['jingbi'] = self.extract_first(etree_html.xpath('//div[@class="jinbi"]//li[1]/p[2]/text()'))
            # 標(biāo)題
            noval_dict['title'] = self.extract_first(etree_html.xpath('//h2/text()'))
            # 簡介
            noval_dict['content'] = self.extract_first(
                etree_html.xpath('//p[@class="intro ih1"]/text()|//p[@class="intro ih2"]/text()'))
            # 作者
            noval_dict['author'] = self.extract_first(etree_html.xpath('//div[@id="ainfo"]/p/span/a[2]/text()'))

            print(noval_dict)
            self.save_data(noval_dict)

    def save_data(self, noval_dict):
        """保存數(shù)據(jù)"""
        pass

    def extract_first(self, data, default=''):
        if len(data) > 0:
            return data[0]
        return default

    def send_request(self, url, header=None, data=None, method="GET"):
        """發(fā)送請求"""
        header = self.default_headers if not header else header

        if method == 'GET':
            # 發(fā)送get請求
            response = requests.get(url=url, params=data, headers=header)
        else:
            # 發(fā)送post請求
            response = requests.post(url=url, data=data, headers=header)

        if response.status_code == 200:
            # 請求成功,返回頁面源碼
            return response.text


if __name__ == '__main__':
    spider = HengYanSpider()
    spider.get_noval_url()

bs4(python獨(dú)有簡單便捷和高效)

環(huán)境安裝:pip install lxml bs4用到lxml庫,如果沒有安裝過lxml庫的時候,需要安裝一下
代碼使用流程:
核心思想:可以將html文檔可以轉(zhuǎn)換成BeautifulSoup對象,調(diào)用該對象中的屬性和方法進(jìn)行
1.導(dǎo)包
from bs4 import BeautifulSoup
2.創(chuàng)建BeautifulSoup對象
a.本地
Beautiful(‘open(‘本地的html文件’)’,‘lxml’)
b.網(wǎng)絡(luò)
Beautiful(‘網(wǎng)絡(luò)請求到的頁面數(shù)據(jù)’,‘lxml’)
屬性和方法:
1.根據(jù)標(biāo)簽名查找
soup.a 只能找到第一個符合要求的標(biāo)簽
2.獲取屬性
soup.a.attrs 獲取a所有的屬性和屬性值,返回一個字典
soup.a.attrs[‘href’] 獲取href屬性
soup.a[‘href’] 也可簡寫為這種形式
3.獲取內(nèi)容
soup.a.string /text()
soup.a.text //text()
soup.a.get_text() //text()
如果標(biāo)簽還是標(biāo)簽,那么string獲取到的結(jié)果為none,而其他兩個,可以獲取文本內(nèi)容
4.find:找到第一個符合要求的標(biāo)簽
soup.find(‘a(chǎn)’) 找到第一個符合要求的
soup.find(‘a(chǎn)’,title=‘xxx’)
soup.find(‘a(chǎn)’,alt=‘xxx’)
soup.find(‘a(chǎn)’,class=‘xxx’)
soup.find(‘a(chǎn)’,id=‘xxx’)
5.find_All:找到所有符合要求的標(biāo)簽
soup.find_All(‘a(chǎn)’)
soup.find_All([‘a(chǎn)’,‘b’]) 找到所有的a和b標(biāo)簽
soup.find_All(‘a(chǎn)’,limit=2) 限制前兩個
6.根據(jù)選擇器選擇指定的內(nèi)容
select:soup.select(’#feng’)
常見的選擇器:標(biāo)簽選擇器(a)、類選擇器(.)、id選擇器(#)、層級選擇器
層級選擇器:
div .dudu #lala .name .xixi 下面好多級 div//img
div > p > a > .lala 只能是下面一級 div/img
select選擇器返回永遠(yuǎn)是列表,需要通過下標(biāo)提取指定對象
class HengYanSpider(object):

    def __init__(self):
        self.first_url = 'http://all.hengyan.com/1/0_0_0_0_0_0_0_0_0_1.aspx'
        self.default_headers = {
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
        }

    def get_noval_url(self, url=None):
        url = self.first_url if not url else url
        """獲取小說詳情的url地址"""
        html = self.send_request(url)
        if html:
            bs_soup = BeautifulSoup(html, 'lxml')
            lis = bs_soup.find_all(name='li', attrs={'class': 'bookname'})
            for li in lis:
                # a_list = li.find_all(name='a')
                # if len(a_list) > 0:
                #     url = a_list[0].attrs['href']
                a_list = li.select('a')
                if len(a_list) > 0:
                    url = a_list[0].attrs['href']
                    self.get_noval_detail(url)

    def get_noval_detail(self, noval_url):
        """獲取書籍詳情的頁面內(nèi)容,解析數(shù)據(jù)"""
        html = self.send_request(noval_url)
        if html:
            # 解析數(shù)據(jù)(獲取xpath解析器)
            bs_soup = BeautifulSoup(html, 'lxml')
            # print('得到了詳情頁面')
            noval_dict = {}
            # 書號
            book_id = bs_soup.select('div.dh p label')[0].get_text()
            noval_dict['book_id'] = re.search('\d+', book_id).group()
            # 熱度
            noval_dict['hot'] = bs_soup.select('p.wendu b')[0].get_text()
            # 火車票
            noval_dict['hot_track'] = bs_soup.select('div.piao p')[1].select('.huocolor')[0].get_text()
            # 冰票
            noval_dict['bing_track'] = bs_soup.select('div.piao p')[1].select('.bingcolor')[0].get_text()
            

            print(noval_dict)
            # self.save_data(noval_dict)

    def save_data(self, noval_dict):
        """保存數(shù)據(jù)"""
        pass

    def extract_first(self, data, default=''):
        if len(data) > 0:
            return data[0]
        return default

    def send_request(self, url, header=None, data=None, method="GET"):
        """發(fā)送請求"""
        header = self.default_headers if not header else header

        if method == 'GET':
            # 發(fā)送get請求
            response = requests.get(url=url, params=data, headers=header)
        else:
            # 發(fā)送post請求
            response = requests.post(url=url, data=data, headers=header)

        if response.status_code == 200:
            # 請求成功,返回頁面源碼
            return response.text

if __name__ == '__main__':
    spider = HengYanSpider()
    spider.get_noval_url()
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容