必應壁紙爬蟲(基於bs4/re/lxml)


可能需要導入的包

import time
import os
import re
import requests
from fake_useragent import UserAgent
from lxml import html as lxml_html
from urllib import parse
from bs4 import BeautifulSoup

1.查看網站結構



1.1 獲取網站response信息

必應壁紙的網站應該是把用f12打開工作臺,右鍵這類的操作通過js禁止了,不過依舊可以通過各種方式來查看網頁結構。

img

img

img

現在確定了,能夠獲取圖片信息的請求地址爲https://bing.ioliu.cn/?p=1,最好把html下來,以此方便之後解析的測試。

# 得到response中的html文件代碼
def get_html():
    ua = UserAgent()

    url = f"https://bing.ioliu.cn/?p=1"
    # 請求頭
    headers = {
        "User-Agent": ua.random,
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        "Accept-Encoding": "gzip, deflate, br",
        "Accept-Language": "zh-CN,zh;q=0.9",
        "Cache-Control": "max-age=0",
        "Host": "bing.ioliu.cn",
    }
    response = requests.get(url=url, headers=headers)
    rs_content = response.content.decode('utf8')
    print(rs_content)

    with open('./bing_wallpaper.html', 'w+', encoding='utf-8') as fp:
        fp.write(rs_content)

    print('ok')
    
get_html()



1.2 觀察規律

img

圖片地址的示例如下:

http://h1.ioliu.cn/bing/AlgonquinGrouse_ZH-CN2514966091_1920x1080.jpg

http://h1.ioliu.cn/bing/LastJedi_ZH-CN8789881870_1920x1080.jpg

img

"""
請求地址
https://bing.ioliu.cn/
https://bing.ioliu.cn/?p=1"
https://bing.ioliu.cn/?p=2"
https://bing.ioliu.cn/?p=3"

壁紙詳情地址
https://bing.ioliu.cn/photo/LastJedi_ZH-CN8789881870?force=home_1

圖片地址
http://h1.ioliu.cn/bing/AlgonquinGrouse_ZH-CN2514966091_1920x1080.jpg
http://h1.ioliu.cn/bing/LastJedi_ZH-CN8789881870_1920x1080.jpg

"""

結論: 每個圖片都有一個id(img_id),例如,LastJedi_ZH-CN8789881870,然後其壁紙1920x1080的拼接規律爲:

http://h1.ioliu.cn/bing/LastJedi_ZH-CN8789881870_1920x1080.jpg

http://h1.ioliu.cn/bing/<img_id>_1920x1080.jpg

由於圖片地址沒有固定規律,應該通過有規律的請求地址獲取html代碼,再清洗提煉出圖片地址



2.提取目標圖片鏈接



2.1 圖片url提取思路

經過觀察,目前至少有兩種方式來提取到:

① 先獲取圖片的img_id,然後拼接

② 直接在html中提取完整的圖片url

img

img



2.2 基於正則表達式提取

def extract01(html):
    '''
    方式: 用xpath提取壁紙鏈接
    :param html: str  需要清洗的html
    :return: dict {圖片名:圖片url}
    '''
    img_url_list = re.findall('pic=(.*?)\\.jpg', html)
    img_title_list = re.findall('<div class="description"><h3>(.*?)</h3>', html)

    treated_img_url_list = []
    for img_url in img_url_list:
        treated_img_url_list.append(img_url + '.jpg')

    treated_img_title_list = []
    for img_title in img_title_list:
        treated_img_title_list.append(img_title.split(' ')[0])

    img_dict = dict(zip(treated_img_title_list, treated_img_url_list))
    print(img_dict)
    return img_dict

結果:

{'極北朱頂雀的巢,芬蘭拉普蘭': 'http://h1.ioliu.cn/bing/ArcticRedpoll_ZH-CN7968973967_1920x1080.jpg',
 '普爾曼附近的帕盧斯一輛拖拉機在耕作時揚起塵土,華盛頓州': 'http://h1.ioliu.cn/bing/PalouseSpring_ZH-CN6803103328_1920x1080.jpg',
 '索爾茲伯裏大教堂與放牧的羊羣,英格蘭': 'http://h1.ioliu.cn/bing/SalisburyCathedral_ZH-CN6366350896_1920x1080.jpg',
 '一隻南美貘幼崽小跑着穿過草地': 'http://h1.ioliu.cn/bing/SouthAmericanTapir_ZH-CN6151058361_1920x1080.jpg',
 '紅寶石海灘的日落,華盛頓州奧林匹克國家公園': 'http://h1.ioliu.cn/bing/RubySunset_ZH-CN5544596519_1920x1080.jpg',
 '福克蘭羣島上的南跳巖企鵝': 'http://h1.ioliu.cn/bing/FalklandRockhoppers_ZH-CN5370686595_1920x1080.jpg',
 '由哈勃太空望遠鏡拍攝的大麥哲倫星雲': 'http://h1.ioliu.cn/bing/MegellanicCloud_ZH-CN5132305226_1920x1080.jpg',
 '正在游水的雄性王絨鴨,挪威特羅姆斯-芬馬克郡': 'http://h1.ioliu.cn/bing/KingEider_ZH-CN3559595357_1920x1080.jpg',
 '懷波瓦森林中一棵名爲Te': 'http://h1.ioliu.cn/bing/KauriTree_ZH-CN3695568740_1920x1080.jpg',
 '黃石國家公園的大棱鏡泉,懷俄明州': 'http://h1.ioliu.cn/bing/GPS_ZH-CN5160095061_1920x1080.jpg',
 'Micheldever': 'http://h1.ioliu.cn/bing/BluebellWood_ZH-CN8128422960_1920x1080.jpg',
 '內斯特角燈塔上空的銀河': 'http://h1.ioliu.cn/bing/NeistPoint_ZH-CN3115403132_1920x1080.jpg'}

2.3 基於xpath提取

def extract02(html):
    '''
    方式: 用xpath提取壁紙鏈接
    :param html: str  需要清洗的html
    :return: dict {圖片名:圖片url}
    '''
    etree = lxml_html.etree
    html_parser = etree.HTML(html)

    # 原始數據
    # 獲取圖片url列表
    img_url_list = html_parser.xpath('//a[@class="ctrl share"]/@href')

    # 獲取圖片標題列表
    img_title_list = html_parser.xpath('//div[@class="description"]/h3/text()')

    # 清洗、提取url
    treated_img_url_list = []
    for img_url in img_url_list:
        params = parse.parse_qs(parse.urlparse(img_url).query)
        treated_img_url_list.append(params['pic'][0].replace('?imageslim', ''))

    # 清洗、提取title來爲圖片命名(解決文件名過長、不能攜帶‘/’等情況)
    treated_img_title_list = []
    for img_title in img_title_list:
        treated_img_title_list.append(
            img_title.split(' ')[0]
        )

    img_dict = dict(zip(treated_img_title_list, treated_img_url_list))
    return img_dict

結果:

{'極北朱頂雀的巢,芬蘭拉普蘭': 'http://h1.ioliu.cn/bing/ArcticRedpoll_ZH-CN7968973967_1920x1080.jpg',
 '普爾曼附近的帕盧斯一輛拖拉機在耕作時揚起塵土,華盛頓州': 'http://h1.ioliu.cn/bing/PalouseSpring_ZH-CN6803103328_1920x1080.jpg',
 '索爾茲伯裏大教堂與放牧的羊羣,英格蘭': 'http://h1.ioliu.cn/bing/SalisburyCathedral_ZH-CN6366350896_1920x1080.jpg',
 '一隻南美貘幼崽小跑着穿過草地': 'http://h1.ioliu.cn/bing/SouthAmericanTapir_ZH-CN6151058361_1920x1080.jpg',
 '紅寶石海灘的日落,華盛頓州奧林匹克國家公園': 'http://h1.ioliu.cn/bing/RubySunset_ZH-CN5544596519_1920x1080.jpg',
 '福克蘭羣島上的南跳巖企鵝': 'http://h1.ioliu.cn/bing/FalklandRockhoppers_ZH-CN5370686595_1920x1080.jpg',
 '由哈勃太空望遠鏡拍攝的大麥哲倫星雲': 'http://h1.ioliu.cn/bing/MegellanicCloud_ZH-CN5132305226_1920x1080.jpg',
 '正在游水的雄性王絨鴨,挪威特羅姆斯-芬馬克郡': 'http://h1.ioliu.cn/bing/KingEider_ZH-CN3559595357_1920x1080.jpg',
 '懷波瓦森林中一棵名爲Te': 'http://h1.ioliu.cn/bing/KauriTree_ZH-CN3695568740_1920x1080.jpg',
 '黃石國家公園的大棱鏡泉,懷俄明州': 'http://h1.ioliu.cn/bing/GPS_ZH-CN5160095061_1920x1080.jpg',
 'Micheldever': 'http://h1.ioliu.cn/bing/BluebellWood_ZH-CN8128422960_1920x1080.jpg',
 '內斯特角燈塔上空的銀河': 'http://h1.ioliu.cn/bing/NeistPoint_ZH-CN3115403132_1920x1080.jpg'}

2.4 使用beautifulshop提取

def extract03(html):
    '''
    方式: 用BeautifulSoup提取壁紙鏈接
    :param html: str 需要清洗的html
    :return: dict {圖片名:圖片url}
    '''
    soup = BeautifulSoup(html, "html.parser")

    img_url_list = soup.find_all("a", class_="ctrl share")
    img_title_list = soup.find_all("div", class_="description")

    treated_img_url_list = []
    for one_a_class in img_url_list:
        params = parse.parse_qs(parse.urlparse(one_a_class.attrs['href']).query)
        treated_img_url_list.append(params['pic'][0].replace('?imageslim', ''))

    # 清洗、提取title來爲圖片命名(解決文件名過長、不能攜帶‘/’等情況)
    treated_img_title_list = []
    for img_title in img_title_list:
        treated_img_title_list.append(
            str(img_title.h3.text).split(' ')[0]
        )
    img_dict = dict(zip(treated_img_title_list, treated_img_url_list))
    return img_dict

結果:

{'極北朱頂雀的巢,芬蘭拉普蘭': 'http://h1.ioliu.cn/bing/ArcticRedpoll_ZH-CN7968973967_1920x1080.jpg',
 '普爾曼附近的帕盧斯一輛拖拉機在耕作時揚起塵土,華盛頓州': 'http://h1.ioliu.cn/bing/PalouseSpring_ZH-CN6803103328_1920x1080.jpg',
 '索爾茲伯裏大教堂與放牧的羊羣,英格蘭': 'http://h1.ioliu.cn/bing/SalisburyCathedral_ZH-CN6366350896_1920x1080.jpg',
 '一隻南美貘幼崽小跑着穿過草地': 'http://h1.ioliu.cn/bing/SouthAmericanTapir_ZH-CN6151058361_1920x1080.jpg',
 '紅寶石海灘的日落,華盛頓州奧林匹克國家公園': 'http://h1.ioliu.cn/bing/RubySunset_ZH-CN5544596519_1920x1080.jpg',
 '福克蘭羣島上的南跳巖企鵝': 'http://h1.ioliu.cn/bing/FalklandRockhoppers_ZH-CN5370686595_1920x1080.jpg',
 '由哈勃太空望遠鏡拍攝的大麥哲倫星雲': 'http://h1.ioliu.cn/bing/MegellanicCloud_ZH-CN5132305226_1920x1080.jpg',
 '正在游水的雄性王絨鴨,挪威特羅姆斯-芬馬克郡': 'http://h1.ioliu.cn/bing/KingEider_ZH-CN3559595357_1920x1080.jpg',
 '懷波瓦森林中一棵名爲Te': 'http://h1.ioliu.cn/bing/KauriTree_ZH-CN3695568740_1920x1080.jpg',
 '黃石國家公園的大棱鏡泉,懷俄明州': 'http://h1.ioliu.cn/bing/GPS_ZH-CN5160095061_1920x1080.jpg',
 'Micheldever': 'http://h1.ioliu.cn/bing/BluebellWood_ZH-CN8128422960_1920x1080.jpg',
 '內斯特角燈塔上空的銀河': 'http://h1.ioliu.cn/bing/NeistPoint_ZH-CN3115403132_1920x1080.jpg'}

3.寫入文件

圖片的話,只需要以二進制的方式寫入文件,並加上圖片的後綴名,即可實現圖片的寫入

headers = {
            "Accept": "image/webp,image/apng,image/*,*/*;q=0.8",
            "Accept-Encoding": "gzip, deflate",
            "Accept-Language": "zh-CN,zh;q=0.9",
            "Connection": "keep-alive",
            "Cookie": "_ga=GA1.2.1461883180.1588215214; _gid=GA1.2.1769196419.1588545211; _gat_gtag_UA_61934506_5=1",
            "Host": "h1.ioliu.cn",
            "User-Agent": str(ua.random)
        }


def download_pic(img_dict):
        '''
        根據img_dict下載並命名img文件
        :param img_dict: dict
        :return:
        '''
        try:
            for img_name, img_url in img_dict.items():
                res = requests.get(img_url, headers=self.headers_02)
                with open(f"{self.dir}\\{img_name}.jpg", mode="wb") as f:
                    f.write(res.content)
                print(f"{img_name}, {img_url}, 下載完成")
        except Exception as e:
            print(img_name)
            print("下載出錯", e)

4.形成爬蟲

import re
import os
import requests
import time
from bs4 import BeautifulSoup
from fake_useragent import UserAgent
from urllib import parse

'''
https://bing.ioliu.cn/?p=1"
https://bing.ioliu.cn/?p=2"
https://bing.ioliu.cn/?p=3"
'''



class BingWallpaperSpider01(object):
    def __init__(self, pages=1, dir_name='BingWallpaper'):
        ua = UserAgent()
        
        # 請求頁面時使用的請求頭
        self.headers_01 = {
            "User-Agent": str(ua.random),
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
            "Accept-Encoding": "gzip, deflate, br",
            "Accept-Language": "zh-CN,zh;q=0.9",
            "Cache-Control": "max-age=0",
            "Host": "bing.ioliu.cn",
        }
        
        # 下載圖片使用的請求頭
        self.headers_02 = {
            "Accept": "image/webp,image/apng,image/*,*/*;q=0.8",
            "Accept-Encoding": "gzip, deflate",
            "Accept-Language": "zh-CN,zh;q=0.9",
            "Connection": "keep-alive",
            "Cookie": "_ga=GA1.2.1461883180.1588215214; _gid=GA1.2.1769196419.1588545211; _gat_gtag_UA_61934506_5=1",
            "Host": "h1.ioliu.cn",
            "User-Agent": str(ua.random)
        }
        self.pages = pages
        self.dir = dir_name

        # 創建圖片存放的文件夾
        if os.path.exists(self.dir):
            print('目錄已存在')
        else:
            os.mkdir(self.dir)
            print('目錄創建成功')


    def get_res_content(self, page=1):
        '''
        獲取請求內容
        :param page: int  要爬取的頁數 一頁12張
        :return: str res_content
        '''
        url = f"https://bing.ioliu.cn/?p={page}"
        res = requests.get(url=url, headers=self.headers_01)
        res_content = res.content.decode('utf8')
        return res_content

    def get_img_dict_01(self, html):
        '''
        方式: 用BeautifulSoup提取壁紙鏈接
        :param html: 需要清洗的html
        :return: dict {圖片名:圖片url}
        '''
        soup = BeautifulSoup(html, "html.parser")

        img_url_list = soup.find_all("a", class_="ctrl share")
        img_title_list = soup.find_all("div", class_="description")

        treated_img_url_list = []
        for one_a_class in img_url_list:
            # 獲取param中[pic]的值
            params = parse.parse_qs(parse.urlparse(one_a_class.attrs['href']).query)
            treated_img_url_list.append(params['pic'][0].replace('?imageslim', ''))

        # 清洗、提取title來爲圖片命名(解決文件名過長、不能攜帶‘/’等情況)
        treated_img_title_list = []
        for img_title in img_title_list:
            treated_img_title_list.append(
                str(img_title.h3.text).split(' ')[0]
            )
        img_dict = dict(zip(treated_img_title_list, treated_img_url_list))
        return img_dict

    def download_pic(self, img_dict):
        '''
        根據img_dict下載並命名img文件
        :param img_dict: dict
        :return:
        '''
        try:
            for img_name, img_url in img_dict.items():
                res = requests.get(img_url, headers=self.headers_02)
                with open(f"{self.dir}\\{img_name}.jpg", mode="wb") as f:
                    f.write(res.content)
                print(f"{img_name}, {img_url}, 下載完成")
        except Exception as e:
            print(img_name)
            print("下載出錯", e)

    def main(self):
        i = 1
        while i <= self.pages:
            print(f"當前第{i}頁,共需要下載{self.pages}頁")
            res_content = self.get_res_content(page=i)
            pics = self.get_img_dict_01(html=res_content)
            self.download_pic(pics)
            i += 1
        print("下載完成")


if __name__ == '__main__':
    spider01 = BingWallpaperSpider01(pages=2)
    spider01.main()

結果:

img

img

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章