Обзор
- в этом блоге мы рассмотрим простой пример использования многопоточности для веб-скрейпинга в python
- Требования
- библиотеки, используемые в данном проекте
beautifulsoup4==4.11.1
requests==2.28.1
urllib3==1.26.10
Синтаксис
- создание потока с помощью класса Thread
Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
- мы не будем создавать пользовательский поток, а воспользуемся этим классом и будем использовать только параметры target, который указывает на функцию, где мы будем применять поток, и параметр args, так как мы будем передавать аргументы в функцию
- направление запросов на веб-сайт
response = requests.get(url)
- установка таймера между запросами, где n — число float или int
time.sleep(n)
- создание нашего супа
soup = BeautifulSoup(response.text,'html.parser')
- поиск селекторов
selector_padre = soup.find('selector',{'class: nombre-de-clase'})
- эта функция вернет первый найденный селектор с указанным именем класса
- если наш селектор содержится в родительском селекторе, мы можем продолжать его искать
- в данном случае мы ищем все селекторы с именем selector-child
.find_all('selector-hijo')
- получение содержимого селектора
.find('title').text
- создание json
with open("output.json", "a") as f:
json.dump(data, f, indent=4)
- важно, чтобы при открытии файла с помощью контекстного менеджера мы перевели его в режим добавления, иначе мы не будем иметь полных результатов, поскольку каждый поток будет переписывать его.
- получение названия потока
current_thread().getName()
Пример
- в этом примере мы собираемся сделать веб-скрейпинг новостного сайта мы сделаем запрос на главную страницу и введем ссылки на категории новостей мы введем категории через потоки, каждый поток будет иметь часть этих ссылок имея 3 потока в общей сложности.
- импортируйте необходимые библиотеки
from bs4 import BeautifulSoup
import requests
from threading import Thread
from threading import current_thread
import time
import json
main: в нашем методе main будет находиться весь код, в нашем скрипте мы не будем обращаться непосредственно к этой функции, а будем делать это через потоки.
def main(x,y):
url = "https://pagina12.com.ar/"
bts = requests.get(url)
soup = BeautifulSoup(bts.text, 'html.parser')
urls = soup.find("div", {"class": "p12-dropdown-column"}).find_all('a', href=True)
for link in urls[x:y]:
r = requests.get(link.get('href'))
time.sleep(1)
s = BeautifulSoup(r.text, 'html.parser')
title = s.find("title").text
with open("links4.json", "a") as f:
json.dump({'title':title,'thread': current_thread().getName()}, f, indent=4)
- главная функция получает два параметра x и y, которые будут использоваться для передачи их в качестве индекса в список урлов.
- первое, что мы имеем внутри основной функции — это url сайта, на котором мы хотим провести веб-скрепинг
- мы делаем запрос к этому url и помещаем его в переменную response
- мы создаем наш «суп», передавая Beautifulsoup файл response.text и парсер, в данном случае html.parser
- мы продолжаем получать урлы, ищем селектор div с классом p12-dropdown-columns на этом div мы ищем все его дочерние селекторы anchors или ‘a’ это вернет список этих селекторов (только из a)
- мы проходим по списку урлов по частям с позициями индексов от x до y, проходя по урлам таким образом, мы можем заставить поток, который проходит через этот цикл, нести свою собственную «порцию» ссылок, и таким образом, когда мы генерируем json, у нас не будет повторяющихся элементов.
- внутри цикла мы сделаем запрос с этими ссылками
- мы ставим таймер после каждого запроса
- мы заново создаем наш суп
- для каждого сайта мы собираемся получить название страницы, которую мы ищем с помощью .find(), и получить содержимое найденного селектора с помощью .text
- мы открываем json с помощью контекстного менеджера в режиме добавления, чтобы избежать перезаписи json
- В json.dump мы передаем данные, которые нам нужны, затем имя, которое мы дали объекту файла, и ставим идентификатор из 4 пробелов.
потоки: запуск потоков вне основной функции
t1 = Thread(target=main, args=(0,10))
t2 = Thread(target=main, args=(10,20))
t3 = Thread(target=main, args=(20,25))
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()
print("Done!")
- создать потоки путем инстанцирования класса Thread(…)
- запускаем поток с помощью t.start()
- завершите его с помощью t.join()