爬蟲初識之BeautifulSoup庫的使用-爬取某圖片站的image


知識背景:

beautifulsoup:是一款非常強大的工具,爬蟲利器。“美味的湯,綠色的濃湯”。一個靈活又方便的網頁解析庫,處理高效,支持多種解析器。 利用它就不用編寫正則表達式也能方便的實現網頁信息的抓取。

lxml:是python的一個解析庫,支持HTML和XML的解析,支持XPath解析方式,而且解析效率非常高。(具體可參考:https://www.cnblogs.com/zhangxinqi/p/9210211.html (XPath:全稱XML Path Language,即XML路徑語言,是一門在XML文檔中查找某部分位置信息的語言,最初是用來搜尋XML文檔節點信息的,但是它同樣適用於HTML文檔的搜索。可參考:https://zhuanlan.zhihu.com/p/37177627 XPath的選擇功能十分強大,它提供了非常簡明的路徑選擇表達式,另外,它還提供了超過100個內建函數,用於字符串、數值、時間的匹配以及節點、序列的處理等,幾乎所有我們想要定位的節點,都可以用XPath來選擇。 XPath於1999年11月16日成為W3C標准,它被設計為供XSLT、XPointer以及其他XML解析軟件使用,更多的文檔可以訪問其官方網站:https://www.w3.org/TR/xpath/

本次demo目的: 熟悉爬蟲,爬取某網站的圖片到本地以便無聊時賞心悅目~~~

准備工作:安裝bs4庫,因用到解析庫lxml,so也要pip install下lxml模塊。(sublime的python環境配置這里就不多做贅述)

參考用例:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8"/>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
        <title>測試bs4</title>
        <meta name="description" content=""/>
        <meta name="keywords" content=""/>
        <link href="" rel="stylesheet"/>
</head>
<body>
    <div>
        <p>python</p>
    </div>
    <div class="song" >
        唐宋八大家
        <a href="http://www.song.com/" title="豪放派" target="_self">
        <p class='su'> <a href="" id='su'>蘇軾</a></p>
        <p>...</p>
        <p>唐有韓柳,宋為歐陽、三蘇和曾王</p>
        </a>
        <a href="" class="du" title="婉約派">李清照</a>
        <p id='li'> <a href="" id='li'>千古第一才女李清照</a></p>
        <img src="http://www.baidu.com/meinv.jpg" alt="">
    </div>

    <div class="tang">
        <ul>
            <li><b>生當作人傑,死亦為鬼雄</b></li>
            <li><i>易安居士</i></li>
            <li><a href="http://www.baidu.com" title="qing">清明時節雨紛紛,路上行人欲斷魂,借問酒家何處有,牧童遙指杏花村</a></li>
            <li><a href="http://www.163.com" title="qin">秦時明月漢時關,萬里長征人未還,但使龍城飛將在,不教胡馬度陰山</a></li>
            <li><a href="http://www.126.com" alt="qi">岐王宅里尋常見,崔九堂前幾度聞,正是江南好風景,落花時節又逢君</a></li>
            <li><a href="http://www.haha.com" id='feng'>鳳凰台上鳳凰游,鳳去台空江自流,吳宮花草埋幽徑,晉代衣冠成古丘</a></li>
            <li><a href="http://www.sina.com" class="du">杜甫</a></li>
            <li><a href="http://www.dudu.com" class="du">杜牧</a></li>
        </ul>
    </div>
</body>
</html>
from bs4 import BeautifulSoup

# 1.生成對象
soup = BeautifulSoup(open('soup_text.html', encoding='utf-8'), 'lxml')
print(soup.prettify())
print(type(soup))
print(soup)
# 標簽選擇器:通過這種soup.標簽名查找,如果文檔中有多個這樣的標簽,只能找到第一個符合要求的標簽
print(soup.title.name)    # 獲取名稱
print(type(soup.title))
print(soup.head)
print(soup.p)
print(soup.div)
# 2.獲取屬性
# soup.a.arrts  # 獲取a標簽所有屬性和值,返回一個字典
# soup.a.attrs['href']  # 獲取href屬性
# soup.a['href']  # 也可簡寫成這種
#上面兩種方式都可以獲取a標簽的href屬性值
print(soup.a['href'])
print(soup.a['title'])
print(soup.a['target'])
print(soup.a.attrs['href'])
#attrs可以傳入字典的方式來查找標簽,但是這里有個特殊的就是class,因為class在python中是特殊的字段,所以如果想要查找class相關的可以更改attrs={'class_':'element'}或者soup.find_all('',{"class":"element}),特殊的標簽屬性可以不寫attrs,例如id
print(soup.find_all(attrs={'id': 'li'}))
# 3.獲取內容
# soup.a.text
# soup.a.get_text()
# soup.a.string  # 只能夠獲取文本,如果標簽里還有標簽就返回None,而前兩個,可以獲取所有文本內容
print(soup.a.get_text())
print(soup.div.text)
print(soup.div.string)
print(soup.div.get_text())
#嵌套選擇
#我們直接可以通過下面嵌套的方式獲取子節點和子孫節點
print(soup.head.title.string)
# 4.find
# soup.find('a')  #找到第一個符合要求的標簽
# print(soup.find('a', title='qin'))
# print(soup.find('a', alt='qi'))
# print(soup.find('a', class_='du'))
# print(soup.find('a', id='feng'))
# print(soup.find('a', class_='du'))  # 注意使用class_,區別於類關鍵字
# div = soup.find('div', class_='tang')
# print(div)
# print(div.find('a', alt='qi'))
print(div.find('a', class_='du'))  # find方法不僅soup可以調用,普通的div對象也能調用,會去指定的div里面去查找符合要求的節點
# 5.find_all
lt = soup.find_all('a')
print('soup.find_all\'s type:', type(lt))
div = soup.find('div', class_='tang')
a = div.find_all('a')
# print(a)
print('soup.find().find_all\'s type:', type(a))  # <class 'bs4.element.ResultSet'>
print('soup.find().find_all element\'s type:', type(a[0]))  # <class 'bs4.element.ResultSet'>
# print(a[0].get_text('href'))
print(div.find_all(['a', 'b']))  # 返回列表內所有的標簽內容
# print(div.find_all('a', limit=2))  # 取前兩個

# find、find_all返回列表,類型都為<class 'bs4.element.ResultSet'>,元素為<class 'bs4.element.Tag'>
# find_all(name,attrs,recursive,text,**kwargs)
# find(name,attrs,recursive,text,**kwargs)
# 可以根據標簽名,屬性,內容查找文檔。
# find返回的匹配結果的第一個元素其他一些類似的用法:
# find_parents()返回所有祖先節點,find_parent()返回直接父節點。
# find_next_siblings()返回后面所有兄弟節點,find_next_sibling()返回后面第一個兄弟節點。
# # find_previous_siblings()返回前面所有兄弟節點,find_previous_sibling()返回前面第一個兄弟節點。
# find_all_next()返回節點后所有符合條件的節點, find_next()返回第一個符合條件的節點
# find_all_previous()返回節點后所有符合條件的節點, find_previous()返回第一個符合條件的節點

# 6.select
s = soup.select('.song')
print(len(s))
print('soup.select\'s type:', type(s))        # 返回列表<class 'list'>
# print(s[0])
print('select\'s element type:', type(s[0]))     # <class 'bs4.element.Tag'>
s1 = soup.select('.song a')    # 組合查找,可層級篩選
print(s1)
print(s1[0].get('href'))
s2 = soup.select('.song img')
print(s2[0].get('src'))
# print(s2[0].get('alt'))
print(soup.select(' p #su'))    # 組合查找,p標簽下id為su的內容
print(soup.select(' p.su'))    # 組合查找,class為su的p標簽內容
print(soup.select('p#li'))    # 組合查找,id為li的p標簽的內容
print(soup.select('head>title'))      # 直接子標簽查找
# select返回列表,類型為<class 'list'>,list中元素為<class 'bs4.element.Tag'>

具體使用:

from bs4 import BeautifulSoup
#設置頭部信息
...
headers={
  'cookie': cookie,      # 自行補充cookies
  'referer': referer,      # 跳轉自的頁面
  'user-agent': user_agent    # 偽裝瀏覽器引擎
}
main_count = 1
# 獲取具體圖片所在url,並下載相應圖片信息
def get_url(url, headers):
    global main_count
    print('******主頁第{}頁,url:{}*****'.format(main_count, url))
    res = requests.get(url, headers=headers)
    soup = BeautifulSoup(res.text, 'lxml')
    div = soup.find('div', class_='main-content')   # 限定div,盡量縮小查找節點的范圍,避免獲取到垃圾url造成不必要麻煩
    for i in div.find_all(target='_blank'):
        if i.select('img'):
            img_url = i.get('href')
            print(img_url)
            # 獲取圖片
            get_second_parse(img_url, headers)
            time.sleep(2)
    # 由於網站圖片集有分頁,所以需要處理分頁,獲取下一圖集所在url,並回調
    if soup.select('a.next.page-numbers')[0].get('href'):
        next_url = soup.select('a.next.page-numbers')[0].get('href')
        main_count += 1
        get_url(next_url, headers)



# 獲取具體圖片所在url,並下載相應圖片信息
def get_url(url, headers, img_num_per_page):
    global main_count
    print('******主頁第{}頁,url:{}*****'.format(main_count, url))
    res = requests.get(url, headers=headers)
    soup = BeautifulSoup(res.text, 'lxml')
    div = soup.find('div', class_='main-content')    # 限定div,盡量縮小查找節點的范圍,避免獲取到垃圾url造成不必要麻煩
    for i in div.find_all(target='_blank'):
        if i.select('img'):
            img_url = i.get('href')
            print(img_url)
            # 獲取圖片
            get_inside_parse(img_url, headers, img_num_per_page)
            # break
            time.sleep(2)
    # 由於網站圖片集有分頁,所以需要處理分頁,獲取下一圖集所在url,並回調
    if soup.select('a.next.page-numbers')[0].get('href'):
        next_url = soup.select('a.next.page-numbers')[0].get('href')
        main_count += 1
        get_url(next_url, headers, img_num_per_page)


# 下載每張照片,注意單位時間訪問服務器的頻率防止被forbidden
def get_inside_parse(lst_url, headers, img_num_per_page):
    count = 1
    print('second_parse')
    res = requests.get(lst_url, headers=headers)
    time.sleep(1)
    try:
        img_title_soup = BeautifulSoup(res.text, 'lxml')
        if img_title_soup.select('.main-title')[0].get_text():
            img_title = img_title_soup.select('.main-title')[0].get_text()
            # print(img_title)
            for index in range(1, img_num_per_page):     # 最多下載到img_num_per_page-1張,可自行設置
                page_url = lst_url + '/' + str(index)
                res = requests.get(page_url, headers=headers)
                time.sleep(1)
                print(page_url, res.status_code)
                if res.status_code != 200:    # 超出圖片列表,status錯誤則跳出
                    break
                soup = BeautifulSoup(res.text, 'lxml')
                # 注意select的使用,select返回值類型:<class 'bs4.element.ResultSet'>列表
                if soup.select('.main-image p a img')[0].get('src'):
                    img_url = soup.select('.main-image p a img')[0].get('src')
                    filename = 'my_imgs/{}'.format(img_title)
                    if not os.path.exists(filename):
                        os.makedirs(filename)
                    print('正在下載{}第{}張圖片'.format(img_title, count))
                    print(soup.select('.main-image p a img')[0].get('src'))
                    print(filename)
                    dirname = '%s/%s.jpg' % (filename, count)
                    with open(dirname, 'wb+') as jpg:
                        jpg.write(requests.get(img_url, headers=headers).content)
                        time.sleep(1)
                count += 1
        else:
            print('URL:{}無main-title').format(lst_url)
    except ValueError as e:
        print(e.ValueError)


if __name__ == '__main__':
    get_url(url, headers, 21)

  

過程問題:

  1. find、find_all返回列表,類型都為<class 'bs4.element.ResultSet'>,元素為<class 'bs4.element.Tag'>

  2. select返回列表,類型為<class 'list'>,list中元素為<class 'bs4.element.Tag'>


免責聲明!

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



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