मैं कैसे तेजी से परिमार्जन कर सकता हूं


16

यहां काम एक एपीआई को एक साइट को परिमार्जन करना है, जो शुरू होता https://xxx.xxx.xxx/xxx/1.jsonहै https://xxx.xxx.xxx/xxx/1417749.jsonऔर इसे सही तरीके से लिखना है। उसके लिए मेरे पास निम्नलिखित कोड है:

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()

लेकिन कार्य करने में बहुत समय लग रहा है। यहां प्रश्न है कि मैं इस प्रक्रिया को कैसे तेज कर सकता हूं।


क्या आपने पहली बार बेंचमार्क करने की कोशिश की कि सिंगल जोंस को प्रोसेस करने में कितना समय लगता है? मान लें कि यह प्रति रिकॉर्ड 300ms लेता है, तो आप इन सभी रिकॉर्डों को क्रमिक रूप से लगभग 5 दिनों में संसाधित कर सकते हैं।
tuxdna

जवाबों:


5

यदि आप मल्टी थ्रेडिंग का उपयोग नहीं करना चाहते हैं तो एसिंको भी एक समाधान है

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()

1
Async का उपयोग करना मल्टी थ्रेडिंग की तुलना में तेजी से काम करता है।
टेक नाथ

प्रतिक्रिया के लिए धन्यवाद। दिलचस्प परिणाम।
फर्स

10

कई चीजें हैं जो आप कर सकते हैं:

  1. पुन: कनेक्शन। नीचे दिए गए मानदंड के अनुसार यह लगभग 3 गुना तेज है
  2. आप समानांतर में कई प्रक्रियाओं में परिमार्जन कर सकते हैं

यहां से समानांतर कोड

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)

पुन: प्रयोज्य कनेक्शन के लिए इस प्रश्न से समय

>>> 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

6

आप दो पहलुओं पर अपना कोड सुधार सकते हैं:

  • ए का उपयोग करना Session, ताकि हर अनुरोध पर एक कनेक्शन फिर से व्यवस्थित न हो और खुला रखा जाए;

  • के साथ अपने कोड में समानता का उपयोग करना asyncio;

यहां देखें https://pawelmhm.github.io/asyncio/python/aiohttp/2016/04/22/asyncio-aiohttp.html


2
क्या आप कुछ और विवरण जोड़ सकते हैं?
टेक नाथ

4

आप जो खोज रहे हैं वह अतुल्यकालिक स्क्रैपिंग है। मैं आपको उरुम के कुछ बैच बनाने का सुझाव दूंगा, अर्थात ५ उरोज (वेबसाइट को क्रेश न करने का प्रयास करें), और उन्हें एसिंक्रोनस परिमार्जन करें। यदि आप एस्बेस्टस के बारे में ज्यादा जानकारी नहीं रखते हैं, तो लिबरी असिनिको के लिए गूगल। मुझे आशा है मैं आपकी मदद कर सकता हूं :)


1
क्या आप कुछ और विवरण जोड़ सकते हैं।
टेक नाथ

3

अनुरोधों को चट करने की कोशिश करें और MongoDB बल्क राइट ऑपरेशन का उपयोग करें।

  • अनुरोध समूह (प्रति समूह 100 अनुरोध)
  • समूहों के माध्यम से Iterate
  • डेटा लाने के लिए अतुल्यकालिक अनुरोध मॉडल का उपयोग करें (एक समूह में URL)
  • समूह पूरा करने के बाद DB अपडेट करें (बल्क राइट ऑपरेशन)

यह निम्नलिखित तरीकों से बहुत समय बचा सकता है * MongoDB विलंबता * तुल्यकालिक नेटवर्क कॉल विलंबता लिखें

लेकिन समानांतर अनुरोध संख्या (चंक आकार) में वृद्धि न करें, यह सर्वर के नेटवर्क लोड को बढ़ाएगा और सर्वर इसे डीडीओएस हमले के रूप में सोच सकता है।

  1. https://api.mongodb.com/python/current/examples/bulk.html

1
क्या आप अनुरोधों और समूह लाने के लिए कोड के साथ मदद कर सकते हैं
Tek Nath

3

यह मानते हुए कि आप एपीआई द्वारा अवरुद्ध नहीं होंगे और कोई दर सीमा नहीं है, इस कोड को प्रक्रिया को 50 गुना तेज करना चाहिए (शायद अधिक क्योंकि सभी अनुरोधों को अब उसी सत्र का उपयोग करके भेजा जाता है)।

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)

2

मैं वही सवाल कई साल पहले हुआ था। मैं अजगर-आधारित उत्तरों से कभी संतुष्ट नहीं हूं, जो बहुत धीमे या बहुत जटिल हैं। जब मैं अन्य परिपक्व साधनों पर स्विच करता हूं, तो गति तेज होती है और मैं कभी वापस नहीं आता।

हाल ही में मैं इस तरह के चरणों का उपयोग प्रक्रिया को गति देने के लिए निम्नानुसार करता हूं।

  1. txt में urls का एक गुच्छा उत्पन्न करें
  2. aria2c -x16 -d ~/Downloads -i /path/to/urls.txtइन फ़ाइलों को डाउनलोड करने के लिए उपयोग करें
  3. स्थानीय स्तर पर पार्स

यह अब तक की सबसे तेज प्रक्रिया है।

वेब पृष्ठों को खुरचने के संदर्भ में, मैं एक बार में एक बार पृष्ठ पर आने के बजाय * .html डाउनलोड करना चाहता हूं, जिससे वास्तव में कोई फर्क नहीं पड़ता। जब आप पृष्ठ पर जाएँ की तरह अजगर उपकरणों के साथ, मारा requestsया scrapyया urllibअभी भी कैश, यह और आप के लिए पूरे वेब सामग्री डाउनलोड करें।


1

सबसे पहले सभी लिंक की सूची बनाएं क्योंकि सभी समान हैं केवल इसे बदल दें।

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)

बस t_no बढ़ाने या घटने से आप थ्रेड्स में से कोई भी नहीं बदल सकते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.