我的第一个爬虫小程序——到底有多少人半途而废?

猜想:看廖雪峰python教程的时候我就在想,这上百篇的教程,到底有多少人能够坚持到最后呢?然后就想到学完一定要把这种半途而废的现象赤裸裸地展现出来,哈哈哈。结果请看图片,不言自明,下降趋势明显。

参考资料

  1. 这是一个人写的爬虫入门教程,我觉得很适合入门
  2. Python 爬虫:把廖雪峰教程转换成 PDF 电子书
  3. 《python编程:从入门到实践》第15章开始有讲怎么画图

步骤方法:
1、请详细耐心看完以上的几篇入门文章之后
2、所有教程的链接在第一篇教程的左边,我们需要获取所有的链接,这个在参考文章2里面有说到,请查看。


3、阅读量按F12,然后Ctrl+F,输入阅读量的数字,发现这个数字在网页中的位置,于是我们知道这在x-wiki-info这个class中,通过beautifulsoup的查找方法,我们可以得到这个标签,之后就很容易得到这个数字了

4、最后是把数字以折线图的形式画出来,请看参考文档3
5、之后学了异步之后改为异步请求多个网页,看看效果怎么样,见代码2
40个网站在相同的网速之下异步和同步所花时间之比为2:3
6、上个步骤容易导致下载的网速过快,然后就被封掉了,从这篇文章的最后教我们用协程的信号量来控制协程的个数,这样子就可以避免上述问题了。见代码3

  • 代码1:同步执行源码:
import requests
from bs4 import BeautifulSoup
import matplotlib.pyplot as plt
import time
import os

START_URL = 'https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000'
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'

# 时间测量的装饰器
def timing(f):
    def wrap(*args):
        time1_s = time.time()
        ret = f(*args)
        time2_s = time.time()
        print('%s function took %0.3f s' % (f.__name__, (time2_s-time1_s)))
        return ret
    return wrap

@timing
def get_all_urls():
    # 要模拟浏览器登陆
    headers = {'User-Agent': USER_AGENT}
    response = requests.get(START_URL,headers=headers)
    # print(response.content.decode())

    bsobj = BeautifulSoup(response.content, 'lxml')
    urls = bsobj.find('ul',{'class':'uk-nav uk-nav-side','style':'margin-right:-15px;'}).find_all('li')
    return urls

@timing
def ReadNum(url):
    # print(url)
    # 要模拟浏览器登陆
    headers = {'User-Agent': USER_AGENT}
    response = requests.get(url, headers=headers)
    # print(response.content.decode())
    soup = BeautifulSoup(response.content, 'lxml')
    ReadInfo = soup.find(class_='x-wiki-info')
    num = int(ReadInfo.span.string.split(':')[1])
    return num
    # print(num)


def main():
    all_readInfo = []
    urls = get_all_urls()

    # ReadNum('https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000')

    urls_num = len(urls)
    for i,url in enumerate(urls):
        # if i <= 5:
        num = ReadNum('https://www.liaoxuefeng.com' + urls[i].a['href'])
        if num is not None:
            all_readInfo.append(num)
        print('还剩下',urls_num - i)
        # else:
        #     break

    plt.plot(all_readInfo)
    plt.show()

代码2:异步执行源码,python版本要大于3.4

import requests
from bs4 import BeautifulSoup
import matplotlib.pyplot as plt
import time
import aiohttp
import asyncio

START_URL = 'https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000'
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'
READING_INFO = []
NUM_LEFT = 0

# 时间测量的装饰器
def timing(f):
    def wrap(*args):
        time1_s = time.time()
        ret = f(*args)
        time2_s = time.time()
        print('%s function took %0.3f s' % (f.__name__, (time2_s-time1_s)))
        return ret
    return wrap

@timing
def get_all_urls():
    # 要模拟浏览器登陆
    headers = {'User-Agent': USER_AGENT}
    response = requests.get(START_URL,headers=headers)
    # print(response.content.decode())

    bsobj = BeautifulSoup(response.content, 'html.parser')
    tags = bsobj.find('ul',{'class':'uk-nav uk-nav-side','style':'margin-right:-15px;'}).find_all('a')

    urls = []
    for tag in tags:
        urls.append('https://www.liaoxuefeng.com' + tag['href'])
    # print(urls)
    return urls


def read_num(url):
    # print(url)
    # 要模拟浏览器登陆
    headers = {'User-Agent': USER_AGENT}
    response = requests.get(url, headers=headers)
    # print(response.content.decode())
    soup = BeautifulSoup(response.content, 'html.parser')
    ReadInfo = soup.find(class_='x-wiki-info')
    num = int(ReadInfo.span.string.split(':')[1])
    return num
    # print(num)


async def read_num_asyncio(url,index):
    # 要模拟浏览器登陆
    headers = {'User-Agent': USER_AGENT}
    async with aiohttp.ClientSession() as client:
        async with client.get(url, headers=headers) as resp:
            assert resp.status == 200
            txt = await resp.text()

    # async with aiohttp.get(url, headers=headers) as resp:
    #     assert resp.status == 200
    #     txt = await resp.text()
    #     # print(txt)

    soup = BeautifulSoup(txt,'html.parser')
    read_info = soup.find(class_='x-wiki-info')
    num = int(read_info.span.string.split(':')[1])
    # print(num)
    global NUM_LEFT,READING_INFO
    READING_INFO[index] = num
    NUM_LEFT += 1
    print(NUM_LEFT)
    # return num


# 异步方法读取100+个教程网页然后提取出阅读量
def asyncio_get_reading_num(urls):
    loop = asyncio.get_event_loop()
    tasks = [read_num_asyncio(url,index) for index,url in enumerate(urls) if index < 20]
    loop.run_until_complete(asyncio.wait(tasks))
    loop.close()


@timing
def main():
    urls = get_all_urls()
    print(len(urls))


    global READING_INFO
    READING_INFO = [0] * len(urls)

    asyncio_get_reading_num(urls)


    # all_readInfo = []
    # urls_num = len(urls)
    # for i,url in enumerate(urls):
    #     if i <= 40:
    #         num = read_num(url)
    #         if num is not None:
    #             all_readInfo.append(num)
    #         print(i)
    #     else:
    #         break

    # plt.plot(READING_INFO)
    # plt.show()

if __name__ == '__main__':
    main()

  • 代码3:用协程信号量来限制协程运行个数
import requests
from bs4 import BeautifulSoup
import matplotlib.pyplot as plt
import time
import aiohttp
import asyncio

START_URL = 'https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000'
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'
READING_INFO = []
NUM_LEFT = 0

# 时间测量的装饰器
def timing(f):
    def wrap(*args):
        time1_s = time.time()
        ret = f(*args)
        time2_s = time.time()
        print('%s function took %0.3f s' % (f.__name__, (time2_s-time1_s)))
        return ret
    return wrap

@timing
def get_all_urls():
    # 要模拟浏览器登陆
    headers = {'User-Agent': USER_AGENT}
    response = requests.get(START_URL,headers=headers)
    # print(response.content.decode())

    bsobj = BeautifulSoup(response.content, 'html.parser')
    tags = bsobj.find('ul',{'class':'uk-nav uk-nav-side','style':'margin-right:-15px;'}).find_all('a')

    urls = []
    for tag in tags:
        urls.append('https://www.liaoxuefeng.com' + tag['href'])
    # print(urls)
    return urls


def read_num(url):
    # print(url)
    # 要模拟浏览器登陆
    headers = {'User-Agent': USER_AGENT}
    response = requests.get(url, headers=headers)
    # print(response.content.decode())
    soup = BeautifulSoup(response.content, 'html.parser')
    ReadInfo = soup.find(class_='x-wiki-info')
    num = int(ReadInfo.span.string.split(':')[1])
    return num
    # print(num)


async def read_num_asyncio(url,index,sem):
    # 要模拟浏览器登陆
    headers = {'User-Agent': USER_AGENT}
    with (await sem):
        async with aiohttp.ClientSession() as client:
            async with client.get(url, headers=headers) as resp:
                assert resp.status == 200
                txt = await resp.text()

        # async with aiohttp.get(url, headers=headers) as resp:
        #     assert resp.status == 200
        #     txt = await resp.text()
        #     # print(txt)

        soup = BeautifulSoup(txt,'html.parser')
        read_info = soup.find(class_='x-wiki-info')
        num = int(read_info.span.string.split(':')[1])
        # print(num)
        global NUM_LEFT,READING_INFO
        READING_INFO[index] = num
        NUM_LEFT += 1
        print(NUM_LEFT)
        # return num


# 异步方法读取100+个教程网页然后提取出阅读量
def asyncio_get_reading_num(urls):
    # 设置线程的信号量,最多5个协程在工作,根据网站的流量或者实际测试确定
    # 如果没有进行限制,那么中途可能被封IP
    sem = asyncio.Semaphore(5)
    loop = asyncio.get_event_loop()
    # tasks = [read_num_asyncio(url,index,sem) for index,url in enumerate(urls) if index < 20]
    tasks = [read_num_asyncio(url, index, sem) for index, url in enumerate(urls)]
    loop.run_until_complete(asyncio.wait(tasks))
    loop.close()


@timing
def main():
    urls = get_all_urls()
    print(len(urls))


    global READING_INFO
    READING_INFO = [0] * len(urls)

    asyncio_get_reading_num(urls)


    # all_readInfo = []
    # urls_num = len(urls)
    # for i,url in enumerate(urls):
    #     if i <= 40:
    #         num = read_num(url)
    #         if num is not None:
    #             all_readInfo.append(num)
    #         print(i)
    #     else:
    #         break

    plt.plot(READING_INFO)
    plt.show()

if __name__ == '__main__':
    main()
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,601评论 4 369
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,367评论 1 305
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,249评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,539评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,967评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,929评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,098评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,825评论 0 207
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,609评论 1 249
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,796评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,282评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,603评论 3 261
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,277评论 3 242
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,159评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,959评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,079评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,874评论 2 277

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,594评论 25 707
  • 学习传习录大有好处,可以让我知道我是谁?我能做什么?或者又说,我的成就为什么不像我想要的那么大。为什么呢?是我的仁...
    姜天合阅读 445评论 0 3
  • 以下是阅读《精进》的第三章行动中的笔记,以下是具体的反思方法论 (一)信息 做这件事时,我利用了哪些信息? 哪些信...
    欣欣向荣的小天地阅读 226评论 0 0