Asynchrone Erfassung chinesischer Wetterinformationen in der Praxis - Zehntausende Anfragen pro Minute und Single-Thread

Vorwort

Ursprünglich wollte ich Scrapy aktualisieren, aber wie soll ich sagen? Es ist nicht schwierig. Wenn Sie sich die offizielle Dokumentation ansehen, können Sie es im Grunde tun. Der Hauptgrund ist, dass Sie Scrapy nicht gut spielen können, wenn Ihre Crawler-Grundlage nicht gut ist Und für die meisten Leute kann die Installation von Scrapy ein Problem sein, da es einige historische Probleme gibt, schließlich handelt es sich um ein altes Framework von Python2. Natürlich gibt es noch einen anderen Grund: Was ich tun möchte, kann ich nicht mit Scrapy verwenden. Wenn ich Scrapy verwenden kann, muss es ein verteilter Crawler sein, aber was ich hier tun möchte, ist möglicherweise nur ein Client, das heißt, eine Spider-Sammlung.-Software, daher kann dieses Scrapy nicht verwendet werden.

Ziel

Was wir heute tun werden, ist, das Wetter abzurufen, und die verwendete API ist China Weather Network.

BaseUrl = "http://wthrcdn.etouch.cn/weather_mini?city={}"

Es gibt auch viele Online-Crawler, die das China Weather Network direkt crawlen, aber ich verstehe einfach nicht, warum ich auf die Webseite gehen muss und dann zu xpath oder normal, um dies zu tun, offensichtlich alle Daten aus dem dieselbe API wird verwendet, I Warum auf die Seite gehen, um die Daten aus den gerenderten Ergebnissen rückwärts zu parsen? Kann ich die Daten einfach direkt abrufen?

Anforderungsformat

Hier hinten ist unsere Schnittstelle eine Get-Anfrage. Dann müssen wir nur die Stadt oder Nummer in das Feld der Stadt eingeben, und das Rückgabeergebnis ist json. Nachdem wir dieses Ding in ein Wörterbuch umgewandelt haben, sieht es so aus

{
    
    'data':
 {
    
    'yesterday': 
 {
    
    'date': '5日星期六', 'high': '高温 16℃', 'fx': '东北风', 'low': '低温 9℃', 'fl': '<![CDATA[3级]]>', 'type': '多云'}, 
 'city': '九江',
  'forecast': [{
    
    'date': '6日星期天', 'high': '高温 12℃', 'fengli': '<![CDATA[3级]]>', 'low': '低温 7℃', 'fengxiang': '东北风', 'type': '中雨'}, 
 {
    
    'date': '7日星期一', 'high': '高温 14℃', 'fengli': '<![CDATA[2级]]>', 'low': '低温 7℃', 'fengxiang': '北风', 'type': '多云'}, 
 {
    
    'date': '8日星期二', 'high': '高温 19℃', 'fengli': '<![CDATA[2级]]>', 'low': '低温 8℃', 'fengxiang': '东南风', 'type': '晴'}, 
 {
    
    'date': '9日星期三', 'high': '高温 21℃', 'fengli': '<![CDATA[2级]]>', 'low': '低温 11℃', 'fengxiang': '东南风', 'type': '晴'},
 {
    
    'date': '10日星期四', 'high': '高温 23℃', 'fengli': '<![CDATA[1级]]>', 'low': '低温 11℃', 'fengxiang': '南风', 'type': '多云'}
 ], 
 'ganmao': '感冒多发期,适当减少外出频率,适量补充水分,适当增减衣物。', 'wendu': '8'}, 'status': 1000, 'desc': 'OK'}

Anforderungslimit

Ich muss hier sagen, dass die Schnittstelle von China Weather Network yyds überhaupt keine Einschränkungen hat. Nun, was ich tun möchte, ist, Wetterinformationen im ganzen Land zu erhalten, einschließlich Kreisstädten, Tausenden von großen und kleinen Kreisstädten in China, und sie nach Zeitraum zu analysieren, damit der tägliche Anfragebesuch mindestens 2 Wochen beginnt. Wenn es eine Grenze gibt, müssen wir das Antiklettern umkehren, aber durch meinen Test gibt es kein Problem.

fordert nicht-asynchronen Abruf an

Komm, lass uns erstmal einen Vergleich machen, ohne Vergleich schadet es ja nicht, richtig, weil es sehr einfach ist, gehe ich direkt zum Code.

import requests
from datetime import datetime

class GetWeather(object):

    urlWheather = "http://wthrcdn.etouch.cn/weather_mini?city={}"
    requests = requests
    error = {
    
    }
    today = datetime.today().day
    weekday = datetime.today().weekday()
    week = {
    
    0:"星期一",1:"星期二",2:"星期三",3:"星期四",4:"星期五",5:"星期六",6:"星期天"}

    def __getday(self)->str:
        day = str(self.today)+"日"+self.week.get(self.weekday)
        return day


    def get_today_wheather(self,city:str)->dict:

        data = self.getweather(city)
        data = data.get("data").get("forecast")
        today = self.__getday()
        for today_w in data:
            if(today_w.get("date")==today):
                return today_w

    def getweather(self,city:str,timeout:int=3)->dict:
        url = self.urlWheather.format(city)
        try:
            resp = self.requests.get(url,timeout=timeout)
            jsondata =  resp.json()
            return jsondata
        except Exception as e:
            self.error['error'] = "天气获取异常"
            return self.error
    def getweathers(self,citys:list,timeout:int=3):
        wheathers_data = {
    
    }
        for city in citys:
            url = self.urlWheather.format(city)
            try:
                resp = self.requests.get(url=url,timeout=timeout)
                wheather_data = resp.json()
                wheathers_data[city]=wheather_data
            except Exception as e:
                self.error['error'] = "天气获取异常"
                return self.error

        return wheathers_data



if __name__ == '__main__':
    getwheather = GetWeather()

    start = time.time()
    times = 1
    for i in range(5000):
        data = getwheather.get_today_wheather("九江")
        if((times%100==0)):
            print(data,"第",times,"次访问")
        times+=1

    print("访问",times,"次耗时",time.time()-start,"秒")

Für diesen Code habe ich eine einfache Kapselung vorgenommen.
Werfen wir einen Blick auf die Ergebnisse, wie lange haben 5000 Besuche gedauert?
Bildbeschreibung hier einfügen

Hier besuchte ich dieselbe Stadt Jiujiang 5000 Mal

Asynchroner Abruf

Ich habe diesen Code nicht gekapselt, daher sieht er chaotisch aus.
Hier sind einige Punkte zu beachten

Systemobergrenze

Aus diesem Grund ist Asynchron immer noch eine untere Schicht des verwendeten Betriebssystems, also hat diese Parallelität eine Obergrenze, da diese Coroutine asynchron ist und kontinuierlich umgeschaltet werden muss, richtig? Es sieht ein bisschen wie Pythons eigenes Multi-Threading aus, aber dieses "Multi-Threading" wird nur umgeschaltet, wenn IO abgeschlossen ist, andernfalls wird es nicht umgeschaltet.
Also yo, schränke es ein
Bildbeschreibung hier einfügen
Bildbeschreibung hier einfügen

Kodierung

import time

import aiohttp
from datetime import datetime
import asyncio

BaseUrl = "http://wthrcdn.etouch.cn/weather_mini?city={}"

WeekIndex = {
    
    0:"星期一",1:"星期二",2:"星期三",3:"星期四",4:"星期五",5:"星期六",6:"星期天"}

today = datetime.today().day
day = str(today)+"日"+WeekIndex.get(datetime.today().weekday())

TIMES = 0

async def request(city:str,semaphore:asyncio.Semaphore,timeout:int = 3):
    url = BaseUrl.format(city)
    try:
        async with semaphore:
            async with aiohttp.request("GET", url) as resp:
                data = await resp.json(content_type='')
                return data
    except Exception as e:
        raise e


def getwheater(task):
    data = task.result()
    return data

def get_today_weather(task):
    global TIMES
    data = task.result() #得到返回结果

    data = data.get("data").get("forecast")

    for today_w in data:
        if (today_w.get("date") == day):
            TIMES+=1#只有IO操作的时候才会切换,所以这个++操作还是一个原子性操作
            if(TIMES%100==0):
                print(today_w,"第",TIMES,"次访问")
            return today_w



if __name__ == '__main__':
    semaphore = asyncio.Semaphore(500)
    #操作系统上限是同一个时刻509/1024个并发,windows509 linux 1024
    start = time.time()
    tasks = []
    for i in range(5000):
        c = request("九江",semaphore,3)
        task = asyncio.ensure_future(c)
        task.add_done_callback(get_today_weather)
        tasks.append(task)
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(tasks))
    print("耗时",time.time() - start,"秒")

Bildbeschreibung hier einfügen

Ich denke du magst

Origin blog.csdn.net/FUTEROX/article/details/123314500
Empfohlen
Rangfolge