Le travail ici consiste à supprimer une API d'un site qui commence à partir de https://xxx.xxx.xxx/xxx/1.json
à https://xxx.xxx.xxx/xxx/1417749.json
et l'écrire exactement sur mongodb. Pour cela j'ai le code suivant:
client = pymongo.MongoClient("mongodb://127.0.0.1:27017")
db = client["thread1"]
com = db["threadcol"]
start_time = time.time()
write_log = open("logging.log", "a")
min = 1
max = 1417749
for n in range(min, max):
response = requests.get("https:/xx.xxx.xxx/{}.json".format(str(n)))
if response.status_code == 200:
parsed = json.loads(response.text)
inserted = com.insert_one(parsed)
write_log.write(str(n) + "\t" + str(inserted) + "\n")
print(str(n) + "\t" + str(inserted) + "\n")
write_log.close()
Mais cela prend beaucoup de temps pour faire la tâche. La question ici est de savoir comment puis-je accélérer ce processus.
asyncio est également une solution si vous ne souhaitez pas utiliser le multi-threading
import time
import pymongo
import json
import asyncio
from aiohttp import ClientSession
async def get_url(url, session):
async with session.get(url) as response:
if response.status == 200:
return await response.text()
async def create_task(sem, url, session):
async with sem:
response = await get_url(url, session)
if response:
parsed = json.loads(response)
n = url.rsplit('/', 1)[1]
inserted = com.insert_one(parsed)
write_log.write(str(n) + "\t" + str(inserted) + "\n")
print(str(n) + "\t" + str(inserted) + "\n")
async def run(minimum, maximum):
url = 'https:/xx.xxx.xxx/{}.json'
tasks = []
sem = asyncio.Semaphore(1000) # Maximize the concurrent sessions to 1000, stay below the max open sockets allowed
async with ClientSession() as session:
for n in range(minimum, maximum):
task = asyncio.ensure_future(create_task(sem, url.format(n), session))
tasks.append(task)
responses = asyncio.gather(*tasks)
await responses
client = pymongo.MongoClient("mongodb://127.0.0.1:27017")
db = client["thread1"]
com = db["threadcol"]
start_time = time.time()
write_log = open("logging.log", "a")
min_item = 1
max_item = 100
loop = asyncio.get_event_loop()
future = asyncio.ensure_future(run(min_item, max_item))
loop.run_until_complete(future)
write_log.close()
Vous pouvez faire plusieurs choses:
Code parallèle de ici
from threading import Thread
from Queue import Queue
q = Queue(concurrent * 2)
for i in range(concurrent):
t = Thread(target=doWork)
t.daemon = True
t.start()
try:
for url in open('urllist.txt'):
q.put(url.strip())
q.join()
except KeyboardInterrupt:
sys.exit(1)
Timings de cette question pour une connexion réutilisable
>>> timeit.timeit('_ = requests.get("https://www.wikipedia.org")', 'import requests', number=100)
Starting new HTTPS connection (1): www.wikipedia.org
Starting new HTTPS connection (1): www.wikipedia.org
Starting new HTTPS connection (1): www.wikipedia.org
...
Starting new HTTPS connection (1): www.wikipedia.org
Starting new HTTPS connection (1): www.wikipedia.org
Starting new HTTPS connection (1): www.wikipedia.org
52.74904417991638
>>> timeit.timeit('_ = session.get("https://www.wikipedia.org")', 'import requests; session = requests.Session()', number=100)
Starting new HTTPS connection (1): www.wikipedia.org
15.770191192626953
Vous pouvez améliorer votre code sur deux aspects:
Utiliser un Session
, afin qu'une connexion ne soit pas réorganisée à chaque demande et reste ouverte;
Utiliser le parallélisme dans votre code avec asyncio
;
Jetez un œil ici https://pawelmhm.github.io/asyncio/python/aiohttp/2016/04/22/asyncio-aiohttp.html
Ce que vous recherchez probablement, c'est le grattage asynchrone. Je vous recommanderais de créer des lots d'URL, c'est-à-dire 5 URL (essayez de ne pas effacer le site Web), et de les gratter de manière asynchrone. Si vous ne savez pas grand-chose sur async, google pour l'asyncio libary. J'espère pouvoir vous aider :)
Essayez de fragmenter les demandes et utilisez l'opération d'écriture en bloc MongoDB.
Cela peut vous faire gagner beaucoup de temps des manières suivantes * Latence d'écriture MongoDB * Latence des appels réseau synchrones
Mais n'augmentez pas le nombre de requêtes parallèles (taille de bloc), cela augmentera la charge réseau du serveur et le serveur pourrait penser cela comme une attaque DDoS.
En supposant que vous ne serez pas bloqué par l'API et qu'il n'y a pas de limite de débit, ce code devrait rendre le processus 50 fois plus rapide (peut-être plus car toutes les demandes sont désormais envoyées en utilisant la même session).
import pymongo
import threading
client = pymongo.MongoClient("mongodb://127.0.0.1:27017")
db = client["thread1"]
com = db["threadcol"]
start_time = time.time()
logs=[]
number_of_json_objects=1417750
number_of_threads=50
session=requests.session()
def scrap_write_log(session,start,end):
for n in range(start, end):
response = session.get("https:/xx.xxx.xxx/{}.json".format(n))
if response.status_code == 200:
try:
logs.append(str(n) + "\t" + str(com.insert_one(json.loads(response.text))) + "\n")
print(str(n) + "\t" + str(inserted) + "\n")
except:
logs.append(str(n) + "\t" + "Failed to insert" + "\n")
print(str(n) + "\t" + "Failed to insert" + "\n")
thread_ranges=[[x,x+number_of_json_objects//number_of_threads] for x in range(0,number_of_json_objects,number_of_json_objects//number_of_threads)]
threads=[threading.Thread(target=scrap_write_log, args=(session,start_and_end[0],start_and_end[1])) for start_and_end in thread_ranges]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
with open("logging.log", "a") as f:
for line in logs:
f.write(line)
J'ai eu la même question il y a de nombreuses années. Je ne suis jamais satisfait des réponses basées sur python, qui sont assez lentes ou trop compliquées. Après avoir basculé vers d'autres outils matures, la vitesse est rapide et je ne reviens jamais.
Récemment, j'utilise ces étapes pour accélérer le processus comme suit.
aria2c -x16 -d ~/Downloads -i /path/to/urls.txt
pour télécharger ces fichiersC'est le processus le plus rapide à ce jour.
En ce qui concerne le raclage des pages Web, je télécharge même le fichier * .html nécessaire, au lieu de visiter la page une fois à la fois, ce qui ne fait aucune différence. Lorsque vous appuyez sur visiter la page, avec python outils comme requests
ou scrapy
ou urllib
, il cache toujours et télécharge tout le contenu Web pour vous.
Créez d'abord une liste de tous les liens, car tous sont identiques, modifiez-la simplement.
list_of_links=[]
for i in range(1,1417749):
list_of_links.append("https:/xx.xxx.xxx/{}.json".format(str(i)))
t_no=2
for i in range(0, len(list_of_links), t_no):
all_t = []
twenty_links = list_of_links[i:i + t_no]
for link in twenty_links:
obj_new = Demo(link,)
t = threading.Thread(target=obj_new.get_json)
t.start()
all_t.append(t)
for t in all_t:
t.join()
class Demo:
def __init__(self, url):
self.json_url = url
def get_json(self):
try:
your logic
except Exception as e:
print(e)
En augmentant ou en diminuant simplement t_no, vous pouvez modifier le nombre de threads.