Acquisition asynchrone d'informations météorologiques chinoises dans la pratique - des dizaines de milliers de requêtes par minute et un seul thread

avant-propos

Je voulais à l'origine mettre à jour scrapy, mais comment puis-je le dire ? Ce n'est pas difficile. En regardant la documentation officielle, vous pouvez le faire. La raison principale est que si votre base de robot n'est pas bonne, vous ne pouvez pas bien jouer à scrapy Et pour la plupart des gens, l'installation de scrapy peut être un problème, car il y a des problèmes historiques, après tout, c'est un ancien framework de python2. Bien sûr, il y a une autre raison. Ce que je veux faire, je ne peux pas utiliser scrapy. Si je peux utiliser scrapy, ce doit être un robot d'exploration distribué, mais ce que je veux faire ici ne peut être qu'un client, c'est-à-dire un logiciel de collection d'araignées, donc ce scrapy ne peut pas être utilisé.

Cible

Ce que nous allons faire aujourd'hui, c'est obtenir la météo, et l'API utilisée est China Weather Network.

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

Il y en a aussi beaucoup en ligne, le crawler qui explore directement le China Weather Network, mais je ne comprends tout simplement pas, pourquoi dois-je aller sur la page Web puis aller sur xpath ou régulier pour le faire, évidemment toutes les données du même api est utilisé, je Pourquoi aller à la page pour inverser l'analyse des données à partir des résultats rendus ? Puis-je simplement obtenir les données directement ?

format de demande

De retour ici, notre interface est une requête get. Ensuite, nous n'avons qu'à mettre la ville ou le numéro dans le champ de la ville, et le résultat de retour est json. Après avoir transformé cette chose en dictionnaire, cela ressemble à ceci

{
    
    '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'}

limite de demande

Je dois dire ici que l'interface de China Weather Network yyds n'a aucune restriction. Pourquoi, ce que je veux faire, c'est obtenir des informations météorologiques à travers le pays, y compris les villes de comté, des milliers de villes de comté grandes et petites en Chine, et les analyser par période, de sorte que la visite de demande quotidienne commence au moins 2w. S'il y a une limite, il faut inverser l'anti-escalade, mais grâce à mon test, il n'y a pas de problème.

demande une récupération non asynchrone

Allez, faisons d'abord une comparaison, il n'y a pas de mal sans comparaison, d'accord, car c'est très simple, je vais aller directement au 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,"秒")

Pour ce code, j'ai fait une simple encapsulation.
Voyons les résultats, combien de temps ont duré 5000 visites ?
insérez la description de l'image ici

Ici, j'ai visité la même ville Jiujiang 5000 fois

Extraction asynchrone

Je n'ai pas encapsulé ce code, il a donc l'air désordonné.
Voici quelques points à noter

Limite supérieure du système

Pour cette raison, l'asynchrone est toujours une couche inférieure du système d'exploitation utilisé, donc cette simultanéité a une limite supérieure, car cette coroutine est asynchrone et doit être commutée en permanence, n'est-ce pas ? Cela ressemble un peu au propre multi-threading de python, mais ce "multi-threading" ne basculera que lorsque l'IO sera terminé, sinon il ne basculera pas.
Alors yo, limite-le
insérez la description de l'image ici
insérez la description de l'image ici

codage

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,"秒")

insérez la description de l'image ici

Je suppose que tu aimes

Origine blog.csdn.net/FUTEROX/article/details/123314500
conseillé
Classement