Реализация многопоточного сканера (вкл.)

задняя часть Python GitHub рептилия

Эта статья была впервые опубликована вЗнай почти

Основное время работы сканера занимает блокировка ввода-вывода при запросе веб-страницы, поэтому включение многопоточности для одновременного ожидания разных запросов может значительно повысить эффективность работы сканера.

Эта статья основана на многопоточности (здесь открыто 10 потоков), использовании API github, сборе всей информации о более чем 5000 проектах проекта fork cpython и сохранении данных в файле json.

Возьмите этот контент github, впредыдущий постВерсия, не использующая многопоточность, показана на , а вот ее прямое улучшение.

Технология, необходимая для рептилий

  • Библиотека запросов запрашивает веб-страницы, получает данные в формате json, анализирует словарь для извлечения нужной информации и сохраняет файл json.
  • Используйте многопоточность для разработки многопоточности для части запроса на веб-странице.
  • Используйте две очереди для хранения URL-адреса для сканирования и проанализированных данных результатов соответственно.
  • Если у вас есть учетная запись github, вам необходимо указать номер учетной записи и пароль в коде
  • Разбираться в декораторе (здесь только для расчета времени работы, не беда, если вы его не понимаете)

Код сканера выглядит следующим образом

import requests
import time
from threading import Thread
from queue import Queue
import json

def run_time(func):
    def wrapper(*args, **kw):
        start = time.time()
        func(*args, **kw)
        end = time.time()
        print('running', end-start, 's')
    return wrapper


class Spider():

    def __init__(self):
        self.qurl = Queue()
        self.data = list()
        self.email = '' # 登录github用的邮箱
        self.password = '' # 登录github用的密码
        self.page_num = 171
        self.thread_num = 10

    def produce_url(self):
        baseurl = 'https://api.github.com/repos/python/cpython/forks?page={}'
        for i in range(1, self.page_num + 1):
            url = baseurl.format(i)
            self.qurl.put(url) # 生成URL存入队列,等待其他线程提取

    def get_info(self):
        while not self.qurl.empty(): # 保证url遍历结束后能退出线程
            url = self.qurl.get() # 从队列中获取URL
            print('crawling', url)
            req = requests.get(url, auth = (self.email, self.password))
            data = req.json()
            for datai in data:
                result = {
                    'project_name': datai['full_name'],
                    'project_url': datai['html_url'],
                    'project_api_url': datai['url'],
                    'star_count': datai['stargazers_count']
                }
                self.data.append(result)

    @run_time
    def run(self):
        self.produce_url()

        ths = []
        for _ in range(self.thread_num):
            th = Thread(target=self.get_info)
            th.start()
            ths.append(th)
        for th in ths:
            th.join()

        s = json.dumps(self.data, ensure_ascii=False, indent=4)
        with open('github_thread.json', 'w', encoding='utf-8') as f:
            f.write(s)

        print('Data crawling is finished.')

if __name__ == '__main__':
    Spider().run()

Читателям нужно толькоSpiderиз__init__, укажите свой собственный адрес электронной почты и пароль github для запуска сканера.

Рептилия описывается следующим образом

1.run_timeФункция — это декоратор, который вычисляет время выполнения программы и воздействует на нее.Spiderобъектrunфункция

2.Spiderсвоего рода

  • __init__инициализировать некоторые константы
  • produce_urlДля производства все URL-адреса, сохраненные вQueueочередьqurlсередина. Более 5000 элементов распределены по 171 странице, и эти 171 URL хранятся в очереди для разбора запросов. На самом деле нет необходимости в обмене данными между несколькими потоками, поэтому вместо этого используйте списокQueueВозможны и очереди.
  • get_infoЗапрос и анализ веб-страницы, а затем многопоточность позволяют выполнять несколько функций одновременно. Функциональная логика: покаqurlеще есть элементы вqurlИзвлеките URL-адрес для анализа запроса и сохраните результат вdataСписок.当队列中没有元素了即退出循环(爬虫结束)。
  • runВызовите функцию для запуска искателя. первый звонокproduce_urlСоздайте очередь URL-адресов для сканирования. Затем запустите указанное количество потоков, каждый поток начинается сqurlНепрерывно извлекают URL для анализа и хранения данных вdataСписок. Подождите, пока очередь URL-адресов не будет проанализирована и не завершится.dataСохраните данные в файле json

результаты сканирования

Результаты ползания показаны следующим образом

Этой программе потребовалось 33 секунды, чтобы запустить 10 потоков для сканирования 171 страницы. существуетэта статьяБез многопоточности это заняло 333 секунды. Чтобы более четко понять улучшение эффективности многопоточности, читатели могут попробовать изменить приведенный выше код самостоятельно.self.page_numиself.thread_num.

Я провел здесь эксперимент,self.page_numЗначение установлено равным 20, то есть всего сканируется 20 страниц.

  • Запуск 2 потоков за 18,51 секунды
  • 7,49 секунды с 5 потоками
  • 3,97 секунды с 10 потоками
  • 2,11 секунды с 20 потоками

один вопрос

Наконец, оставьте вопрос для размышления читателей: в предыдущемэта статьяПосередине тоже добиваемся многопоточного гада, почему раньше код был такой простой, а теперь сложный?

следовать за

В следующей статье о многопоточном сканере будет реализована многопоточность при перелистывании страниц и сканировании второстепенных страниц.

Добро пожаловать, чтобы обратить внимание на мою колонку знаний

Главная страница колонки:программирование на питоне

Каталог столбцов:содержание

Примечания к выпуску:Примечания к выпуску программного обеспечения и пакетов