爬虫入门-爬取表情包

写在前面:这个网站并没有设置很严格的反爬虫机制,但也要对别人善良一些。即使使用狂暴爬虫,也不要多次反复爬取给服务器增加过大的压力

表情包又不够用了?(虽然我从未遇见过这个问题。)快去网上爬取一波吧。我为了练习爬虫编写一个简单的爬虫来从网上获取超大的表情包。

来自zml的成果图片,用的是我的爬虫

我找的表情包来源网址-斗图啦。这个爬取起来很容易,没有反爬虫机制,很适合入门。

先讲一下html吧。网页的基本数据结构是html文件。是由xml的格式扩展的一种超文本标记语言。大概长这个样子:

<标签1 属性1='xxx' 属性2='xxx' ...>
<下一级标签 属性1='xxx'>
</下一级标签>
</标签1>

你获得一个html之后浏览器会去解析它,然后网页就有了你看到的样子。但是html超文本标记是网页最本质的面貌。

不过我也不想写太多关于html的内容,稍微了解一下就可以使用BeautifulSoup解析xml了。

我们还需要知道关于请求网页的一些知识。常用的网页请求有post和get。它们都需要一个url,但是post还需要一个data。这里我们用的就是get。

首先分析一下网页的结构,使用浏览器工具找到图片的<img>标签。我们发现图片的链接就藏在这个标签的属性里面,你可以点进每个连接去看看。一般情况下是哪个点进去是图片就找哪个链接。

斗图啦,复制<img>标签的选择器

下面开始写代码。需要用到python中的几个关键的类(函数):

  • requests.get(url, headers, …):get请求,url是url,header是请求头。返回一个Response对象。
  • bs4.BeautifulSoup.select(xml, selecter):解析xml,selecter是选择器。就是上图中的“复制选择器”。不过复制下来的选择器需要稍加修改以选择全部。
  • re:正则表达式,用于找到图片链接
  • urllib.request.urlretrieve(url, filename):保存图片时需要用到的函数。url是图像的链接,filename是保存到的文件名
  • time.sleep(t):休眠t秒

Import需要的轮子:

from bs4 import BeautifulSoup
import re
import requests
from urllib.request import urlretrieve
import random
import urllib.error

首先我们需要Get网页信息:

URL = 'https://www.doutula.com/photo/list/?page=1'
header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36'}    #header请求头,用于简单的反反爬,不过这个网站好像不需要
response = requests.get(URL, headers=header)

接下来用BeautifulSoup进行xml解析:

soup = BeautifulSoup(response.text)
data = soup.select('#pic-detail > div > div.col-sm-9.center-wrap > div.random_picture > ul > li > div > div > a > img')    #注意这里是所有的表情包图片的选择器,会返回一个列表带有全部的符合条件的标签信息

下面是写的不是很好的一段,用正则表达式来在标签信息里找到源链接:

urls = {}
for i in data:
    i = str(i)
    r = re.search('data-original=".*" refer', i).span()
    #找到源链接
    url = i[r[0] + 15: r[1] - 7]
    r = re.search('alt=".*" class', i).span()    #找到alt标记以命名图片
    alt = i[r[0] + 5: r[1] - 7]
    urls[alt] = url    #在ulrs字典中创建键值对

最后是保存图片:

for i in urls.keys():
    urlretrieve(urls[i], '.\\saves\\' + i + '.gif')
    time.sleep(5)    #其实可以不要

简单的组合上述代码还不够,为了保证不前功尽弃还需要写异常处理。之后大概长这样:

from bs4 import BeautifulSoup
import re
import requests
from urllib.request import urlretrieve
import random
import urllib.error


def main(URL):
    # 获得表情包
    headers = [{
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36 Edg/81.0.416.58'},
        {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36'}]
    header = random.choice(headers)
    strhtml = requests.get(URL, headers=header)
    soup = BeautifulSoup(strhtml.text)
    data = soup.select('#pic-detail > div > div.col-sm-9.center-wrap > div.random_picture > ul > li > div > div > a > img')
    print(data)
    urls = {}
    for i in data:
        try:
            i = str(i)
            r = re.search('data-original=".*" refer', i).span()
            url = i[r[0] + 15: r[1] - 7]
            r = re.search('alt=".*" class', i).span()
            alt = i[r[0] + 5: r[1] - 7]
            urls[alt] = url
        except urllib.error.HTTPError as e:
            print(e.code)
            print(e.reason)
        except urllib.error.URLError as e:
            print(e.reason)
        except AttributeError:
            pass

    print(urls)

    for i in urls.keys():
        try:
            urlretrieve(urls[i], '.\\saves\\' + i[0:10] + '.gif')
            print("saved", x)
        except urllib.error.HTTPError as e:
            print(e.code)
            print(e.reason)
        except urllib.error.URLError as e:
            print(e.reason)
        except OSError:
            urlretrieve(urls[i], '.\\saves\\' + urls[i][-8: -1] + '.gif')
        finally:
            #time.sleep(random.randint(6,16))
            pass

if __name__ == '__main__':
    x = 1
    while True:
        URL = 'https://www.doutula.com/photo/list/?page=' + str(x)
        main(URL)
        print(x)
        x += 1

接下来的内容可能比较可怕:

我发现不要等待时间也没关系,于是为了尽可能地压榨服务器,尽快获取图片,开发了一个狂暴爬虫(好中二的名字)。使用多线程和多进程,多进程获取网页并调用线程下载。注意:这只适用于没有反爬虫机制的网站,即使是这样也有可能被网站管理员手动封ip。

  • process:多进程
  • threading:多线程

我也不太会优化多线程和多进程,所以只能很暴力地写成了下面的样子:

from bs4 import BeautifulSoup
import re
import requests
from urllib.request import urlretrieve
import random
import urllib.error
import threading
from multiprocessing import Process


def get_urls(URL):
    # 获得表情包
    headers = [{
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36 Edg/81.0.416.58'},
        {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36'}]
    header = random.choice(headers)
    strhtml = requests.get(URL, headers=header)
    soup = BeautifulSoup(strhtml.text)
    data = soup.select('#pic-detail > div > div.col-sm-9.center-wrap > div.random_picture > ul > li > div > div > a > img')
    print(data)
    urls = {}
    for i in data:
        try:
            i = str(i)
            r = re.search('data-original=".*" refer', i).span()
            url = i[r[0] + 15: r[1] - 7]
            r = re.search('alt=".*" class', i).span()
            alt = i[r[0] + 5: r[1] - 7]
            urls[alt] = url
        except urllib.error.HTTPError as e:
            print(e.code)
            print(e.reason)
        except urllib.error.URLError as e:
            print(e.reason)
        except AttributeError:
            pass

    print(urls)

    for i in urls.keys():
        t = threading.Thread(target=save_face, args=(urls[i], i))
        t.start()
        print('saving', i)
        print('threadings: ', threading.activeCount())


def save_face(url, name):
    #下载表情包
    try:
        urlretrieve(url, '.\\saves\\' + name[0:10] + '.gif')
    except urllib.error.HTTPError as e:
        print(e.code)
        print(e.reason)
    except urllib.error.URLError as e:
        print(e.reason)
    except OSError:
        urlretrieve(url, '.\\saves\\' + url[-8: -1])



def main_pro(pro_count, page_num, start_at):
    x = 0
    while True:
        URL = 'https://www.doutula.com/photo/list/?page=' + str(pro_count*x + page_num +start_at)
        get_urls(URL)
        x += 1
        print(str(page_num), ':::',str(pro_count*x + page_num +start_at))



if __name__ == '__main__':
    x = int(input('start page?'))
    #开始的页码
    pro_count = int(input('pro_count?'))
    #进程数量
    for i in range(pro_count):
        main = Process(target=main_pro, args=(pro_count, i, x))
        main.start()

这个的速度肯定是很快的,资源占用率不言而喻也很快。但是这种方法只能用于没有反爬虫的网站上。反反爬虫又是一门高深的学问了。不过这个爬虫还是比较简单的,自己完全可以写下来不用抄代码。

发表评论

电子邮件地址不会被公开。 必填项已用*标注