Эта статья была впервые опубликована вЗнай почти
Основное время работы сканера занимает блокировка ввода-вывода при запросе веб-страницы, поэтому включение многопоточности для одновременного ожидания разных запросов может значительно повысить эффективность работы сканера.
Эта статья основана на многопоточности (здесь открыто 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 потоками
один вопрос
Наконец, оставьте вопрос для размышления читателей: в предыдущемэта статьяПосередине тоже добиваемся многопоточного гада, почему раньше код был такой простой, а теперь сложный?
следовать за
В следующей статье о многопоточном сканере будет реализована многопоточность при перелистывании страниц и сканировании второстепенных страниц.
Добро пожаловать, чтобы обратить внимание на мою колонку знаний
Главная страница колонки:программирование на питоне
Каталог столбцов:содержание
Примечания к выпуску:Примечания к выпуску программного обеспечения и пакетов