爬蟲(四)--正則應用,xml,xpath

一、正則應用

貓眼電影項目(封裝)

需求:爬取貓眼電影top100,並篩選出電影名、主演、上映時間、評分信息。

1.取dl
在這裏插入圖片描述
在這裏插入圖片描述
2.取dd
在這裏插入圖片描述
在這裏插入圖片描述
3.取電影名
在這裏插入圖片描述
4.取主演
在這裏插入圖片描述
5.取上映時間
在這裏插入圖片描述
6.取評分
在這裏插入圖片描述

import requests,re,json

class Maoyan():
    def __init__(self,url):
        self.url = url
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
        }
        self.movie_list = []
        self.parse()
    def parse(self):
        # 爬取10頁
        for i in range(10):
            url = self.url+'?offset=%s'%(i*10)
            response = requests.get(url,headers=self.headers)
            # print(response.text)
            # 提取信息
            # 電影名、主演、上映時間、評分
            dl_pattern = re.compile(r'<dl class="board-wrapper">(.*?)</dl>',re.S)
            dl = dl_pattern.search(response.text).group()
            dd_pattern = re.compile(r'<dd>(.*?)</dd>',re.S)
            dd = dd_pattern.findall(dl)
            movie_name_pattern = re.compile(r'title="(.*?)"')
            role_pattern = re.compile(r'<p class="star">(.*?)</p>',re.S)
            date_pattern = re.compile(r'<p class="releasetime">(.*?)</p>')
            score_pattern = re.compile(r'<i class="integer">(.*?)</i><i class="fraction">(.*?)</i>')
            for one in dd:
                item = {}
                movie_name = movie_name_pattern.search(one).group()
                role = role_pattern.search(one).group(1).strip()[3:]
                date = date_pattern.search(one).group(1)[5:]
                score = score_pattern.search(one).group(1)+score_pattern.search(one).group(2)
                item = {'movie_name':movie_name,'role':role,'date':date,'score':score}
                self.movie_list.append(item)
        # print(self.movie_list)
        # 將電影信息寫入到json文件中
        with open('movie.json','w',encoding='utf-8') as fp:
            json.dump(self.movie_list,fp)

if __name__ == '__main__':
    base_url = 'https://maoyan.com/board/4'
    Maoyan(base_url)
    # 從json文件中讀取信息
    with open('movie.json','r') as fp:
        movie_list = json.load(fp)
    # 就可以任意操作了
    for movie in movie_list:
        print(movie)

json文件儲存的是unicode編碼,這樣更方便傳輸,避免因爲編碼不同而出現的錯誤。
在這裏插入圖片描述

股吧項目

需求:取12頁的閱讀數,評論數,標題,作者,更新時間,詳情頁url信息

1.取ul
在這裏插入圖片描述
在這裏插入圖片描述
2.取li

3.取閱讀數,評論數

4.取標題

5.取作者

6.取更新時間

7.取詳情頁url

import requests,re,json

base_url = 'http://guba.eastmoney.com/default,99_%s.html'
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
}
infos = []
for i in range(1,13):
    response = requests.get(base_url%i,headers=headers)
    ul_pattern = re.compile(r'<ul class="newlist" tracker-eventcode="gb_xgbsy_ lbqy_rmlbdj">(.*?)</ul>',re.S)
    ul = ul_pattern.search(response.text).group(1)
    li_pattern = re.compile(r'<li>(.*?)</li>',re.S)
    li = li_pattern.findall(ul)
    click_comment_pattern = re.compile(r'<cite>(.*?)</cite>(.*?)<cite>(.*?)</cite>',re.S)
    title_pattern = re.compile(r'title="(.*?)" class="note">')
    author_pattern = re.compile(r'<font>(.*?)</font></a>')
    time_pattern = re.compile(r'<cite class="last">(.*?)</cite>')
    url_pattern = re.compile(r'</em> <a href="(.*?)" title="')
    for one in li:
        click = click_comment_pattern.search(one).group(1).strip()
        comment = click_comment_pattern.search(one).group(3).strip()
        if title_pattern.search(one):
            title = title_pattern.search(one).group(1)
        else:
            title = ''
        author = author_pattern.search(one).group(1)
        time = time_pattern.search(one).group(1)
        if url_pattern.search(one):
            url = 'http://guba.eastmoney.com'+url_pattern.search(one).group(1)
        else:
            url = ''
        item = {'click':click,'comment':comment,'title':title,'author':author,'time':time,'url':url}
        infos.append(item)
print(infos)

封裝

import requests,re,json

class Guba():
    def __init__(self,base_url):
        self.base_url = base_url
        self.infos = []
        self.get_info()
    def get_content(self,url):
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
        }
        response = requests.get(url, headers=headers)
        if response.status_code==200:
            return response.text
    def get_info(self):
        for i in range(1, 13):
            html = self.get_content(self.base_url%i)
            ul_pattern = re.compile(
                r'<ul class="newlist" tracker-eventcode="gb_xgbsy_ lbqy_rmlbdj">(.*?)</ul>',re.S)
            ul = ul_pattern.search(html).group(1)
            li_pattern = re.compile(r'<li>(.*?)</li>', re.S)
            li = li_pattern.findall(ul)
            click_comment_pattern = re.compile(
                r'<cite>(.*?)</cite>(.*?)<cite>(.*?)</cite>', re.S)
            title_pattern = re.compile(r'title="(.*?)" class="note">', re.S)
            author_pattern = re.compile(r'<font>(.*?)</font></a>', re.S)
            time_pattern = re.compile(r'<cite class="last">(.*?)</cite>', re.S)
            url_pattern = re.compile(r'</em> <a href="(.*?)" title="', re.S)
            for one in li:
                click = click_comment_pattern.search(one).group(1).strip()
                comment = click_comment_pattern.search(one).group(3).strip()
                if title_pattern.search(one):
                    title = title_pattern.search(one).group(1)
                else:
                    title = ''
                author = author_pattern.search(one).group(1)
                time = time_pattern.search(one).group(1)
                if url_pattern.search(one):
                    url = 'http://guba.eastmoney.com' + url_pattern.search(one).group(1)
                else:
                    url = ''
                item = {'click': click, 'comment': comment, 'title': title,
                        'author': author, 'time': time, 'url': url}
                self.infos.append(item)
        with open('guba_oop.json','w',encoding='utf-8') as fp:
            json.dump(self.infos,fp)
if __name__ == '__main__':
    Guba('http://guba.eastmoney.com/default,99_%s.html')
    with open('guba.json', 'r') as fp:
        info = json.load(fp)
    for one in info:
        print(one)

二、xml

(一)什麼是xml

  • xml稱爲可擴展標記語言
  • xml具有自描述特性,是一種半結構化數據
  • xml的設計宗旨是用來傳輸數據,也可以作爲配置文件記錄一些重要的配置信息

(二)xml和html的區別

1.語法要求不同

  1. 在html中不區分大小寫;xml中嚴格區分

  2. 在html中,如果上下文清楚地顯示出段落或者列表在何處結尾,那麼可以省略</p>或者</li>標記;在xml中,是嚴格的樹狀結構,絕不能省略任何標記

  3. 在xml,擁有單個標記而沒有匹配的結束標記的元素,必須用一個/字符作爲結尾。

    <a href='XXXXXXXX'/>

  4. 在xml中,屬性值必須封裝在引號中;在html中,引號可用可不用。

  5. 在html中,屬性名可以沒有屬性值;xml必須有。

  6. 在xml中,空白部分不會被解析器自動刪除;html是會過濾掉空格的。

2.作用不同

xml主要是用來傳輸數據的。

html主要是用來顯示數據,以及更好的顯示數據的。

3.標記不同

xml沒有固定的標記。

html的標籤是固定的。

三、xpath

(一)什麼是xpath

xpath是一種語法,用來提取xml或者html頁面內容的語法。

(二)xpath語法內容

  • 元素:指一個xml的標籤以及標籤的所有內容

  • 屬性:class、name

  • 內容:a標籤的內容就是‘\n aaa\n 1’和‘\n 2’,b標籤的內容就是bbb

    <a>
    	aaa
    	1<b>bbb</b>
    2</a>
    
  • 標籤:和元素一樣

1.選取節點

  • nodename:選取此標籤的所有子標籤
  • /:從根節點開始選取
  • //:從任意位置開始選取
  • .:選取當前節點
  • …:選取父節點
  • @:選取屬性
  • text():選取內容

2.謂語

英語中的謂語用來限定。

xpath中的謂語也是用來限定選取的內容的。

  • /bookstore/book[1]:表示取bookstore下面的第一個book標籤
  • /bookstore/book[last()]:表示取bookstore下面的最後一個book標籤
  • /bookstore/book[last()-1]:表示取bookstore下面的倒數第二個book標籤
  • /bookstore/book[position()>1]:表示從bookstore下面的第二本書開始取
  • //title[@lang]:取有lang屬性的title標籤
  • //title[@lang=“eng”]:取有lang屬性,且lang屬性值爲eng的title標籤

3.選取未知節點

  • *:匹配任意節點
  • @*:匹配任意屬性

4.選取若干路徑

  • //title|//price:選取所有的title標籤price標籤

四、在python中如何使用xpath

通過lxml模塊,可以使用xpath語法篩選元素。

方法

導包

from lxml import etree

方法一

將字符串形式的xml內容,解析成element對象的方法

html_element = etree.HTML(html_str)

from lxml import etree

text = """
<div>
    <ul>
        <li class="item-0"><a href="link1.html">first item</a><li>
        <li class="item-1"><a href="link2.html">second item</a><li>
        <li class="item-inactive"><a href="link3.html">third item</a><li>
        <li class="item-1"><a href="link4.html">fourth item</a><li>
        <li class="item-0"><a href="link5.html">fifth item</a><li>
    </ul>
</div>
"""
html = etree.HTML(text)
# print(html)    # <Element html at 0x3b62e48>
html_str = etree.tostring(html,pretty_print=True,encoding='utf-8').decode('utf-8')
print(html_str)

:tostring方法的返回值是bytes類型,轉換爲string類型需要解碼。

方法二

將一個html文件轉化成element對象的方法

html = etree.parse('demo.html')

etree.tostring(html,pretty_print=True,encoding='utf-8').decode('utf-8')

當使用parse方法解析一個xml或者html文件時,語法必須嚴格按照xml的標籤語法,且標籤必須對應。

demo.html

<div>
    <ul>
        <li class="item-0"><a href="link1.html">first item</a></li>
        <li class="item-1"><a href="link2.html">second item</a></li>
        <li class="item-inactive"><a href="link3.html"><span class="bold">third item</span>></a></li>
        <li class="item-1"><a href="link4.html">fourth item</a></li>
        <li class="item-0"><a href="link5.html">fifth item</a></li>
    </ul>
</div>

py文件

from lxml import etree

html = etree.parse('demo.html')
# print(html)    # <lxml.etree._ElementTree object at 0x0000000003B51F48>
print(etree.tostring(html,pretty_print=True,encoding='utf-8').decode('utf-8'))

element對象可以調用xpath方法

html_element.xpath(),返回值都是一個存放element對象的列表,還可以繼續調用xpath方法,繼續篩選元素。

from lxml import etree

text = '''
<div>
    <ul>
        <li class="item-0"><a href="link1.html">first item</a></li>
        <li class="item-1"><a href="link2.html">second item</a></li>
        <li class="item-inactive">
         <a href="link3.html" class="a_class_1">aaaa</a>
         <span class="span_item1">span_text1</span>
      </li>
      <li class="item-1">
            test
         <a href="link4.html" class="a_class_0">fourth item
            <span class="span_item2">span_text2</span></a>
      </li>
      <li class="item-0">
         <a href="link5.html" class="bold">fifth item</a>
      </li>
    </ul>
</div>
'''
html = etree.HTML(text)
# print(etree.tostring(html,pretty_print=True,encoding='utf-8').decode('utf-8'))
'''
<html>
  <body><div>
    <ul>
        <li class="item-0"><a href="link1.html">first item</a></li>
        <li class="item-1"><a href="link2.html">second item</a></li>
        <li class="item-inactive">
         <a href="link3.html" class="a_class_1">aaaa</a>
         <span class="span_item1">span_text1</span>
      </li>
      <li class="item-1">
            test
         <a href="link4.html" class="a_class_0">fourth item
            <span class="span_item2">span_text2</span></a>
      </li>
      <li class="item-0">
         <a href="link5.html" class="bold">fifth item</a>
      </li>
    </ul>
</div>
</body>
</html>
'''
# 獲取所有的 <li> 標籤
li_list = html.xpath('//li')
# print(li_list)
# 繼續獲取<li> 標籤的所有 class屬性
li_class_list = html.xpath('//li/@class')
# print(li_class)
# 繼續獲取<li>標籤下href 爲 link1.html 的 <a> 標籤
li_a_href = html.xpath('//li/a[@href="link1.html"]')
# print(li_a_href)
# 獲取<li> 標籤下的所有 <span> 標籤(包括孫子span)
li_span = html.xpath('//li//span')
# print(li_span)
# 獲取 <li> 標籤下的<a>標籤裏的所有 class
li_a_class = html.xpath('//li/a/@class')
# print(li_a_class)
# 獲取最後一個 <li> 的 <a> 的 href
lastli_a_href = html.xpath('//ul/li[last()]/a/@href')
# print(lastli_a_href)
# 獲取倒數第二個元素的內容
con = html.xpath('//*[last()-1]/text()')
# print(con)
# 獲取 class 值爲 bold 的標籤名
bold = html.xpath('//*[@class="bold"]')
print(bold[0].tag)
from lxml import etree

html = '''
<bookstore>
    <title>新華書店</title>
    <book href="http://www.langlang2017.com/">
        <title lang="eng">Harry Potter</title>
        <price>29.99</price>
    </book>
    <book>
        <title lang="zh">Learning XML</title>
        <price>39.95</price>
    </book>
    <book href="www.baidu.com">
        <title>python 大全</title>
        <price>99.95</price>
    </book>
</bookstore>
'''
tree = etree.HTML(html)
# print(etree.tostring(tree,pretty_print=True,encoding='utf-8').decode('utf-8'))
# (1)獲取文檔中的所有book節點的第一個book節點
first_book = tree.xpath('//book[1]')
# print(first_book)
# (2)獲取第一個book節點
first_book = first_book[0]
# (3)獲取first_book當前節點下的href
href = first_book.xpath('.//@href')
# print(href)
# (4)獲取當前節點的父節點下的book節點的href屬性
href2 = first_book.xpath('../book/@href')
# print(href2)
# (5)獲取價格小於40的書的書名
bookname = tree.xpath('//book[price<40]/title/text()')
# print(bookname)
# (6)獲取價格等於99.95的書的書名
bookname2 = tree.xpath('//book[price=99.95]/title/text()')
# print(bookname2)
# (7)獲取book標籤下面的title和price標籤
title_price = tree.xpath('//book/title|//book/price')
# print(title_price)
# (8)獲取屬性href含有baidu字符串的book標籤,獲取此標籤的書名
title = tree.xpath('//book[contains(@href,"baidu")]/title/text()')
# print(title)
# (9)獲取前面兩個屬於bookstore標籤的子標籤的book標籤,獲取此標籤的書名
title2 = tree.xpath('//bookstore/book[position()<3]/title/text()')
# print(title2)
# (10)匹配任意有屬性的節點
result = tree.xpath('//*[@*]')
# print(result)
# (11)獲取所有帶有屬性title標籤,獲取此標籤的內容
result2 = tree.xpath('//title[@*]/text()')
print(result2)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章