Учебное пособие по легальному извлечению данных о вакансиях из Glassdoor без использования какого-либо безголового браузера или входа на сайт.

Glassdoor хранит более 100 миллионов отзывов, зарплат и идей; имеет 2,2 миллиона работодателей, активно размещающих вакансии на рынке, и получает около 59 миллионов уникальных посещений в месяц. С таким большим количеством данных и спросом Glassdoor является золотой жилой для данных о работе и компании.

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

Если говорить о юридических…

Законно ли царапать Glassdoor?

Прямой ответ — да, парсинг Glassdoor является законным, если вы не нарушаете некоторые основные правила.

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

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

При этом все страницы, доступные без учетной записи, считаются общедоступными; следовательно, вы можете очистить эти страницы.

Тем не менее, мы рекомендуем использовать некоторые лучшие методы, чтобы убедиться, что вы относитесь к веб-сайту уважительно и не наносите вреда его пользователям.

Источник: для получения более подробной информации ознакомьтесь с нашим руководством по законности парсинга веб-страниц.

Парсинг заданий Glassdoor в JavaScript

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

Эти страницы категорий общедоступны (они не защищены каким-либо входом в систему или платным доступом), поэтому мы делаем эту 100% белую шляпу.

Требования

Хотя мы объясним каждый шаг процесса, мы предполагаем, что у вас есть базовые знания. Однако, если вы когда-нибудь почувствуете себя потерянным или сбитым с толку, вот несколько более простых проектов веб-скрейпинга, которые вы можете использовать для постепенного наращивания своих навыков:

С этим покончено, давайте начнем с настройки проекта.

1. Получение настройки проекта

Чтобы все заработало, вам нужно создать новую папку для вашего проекта (мы назвали нашу папку glassdoor-scraper) и открыть ее в VS Code или в вашей любимой IDE.

Оказавшись внутри папки, откройте терминал и запустите Node.JS следующим образом:

1npm init -``y

Он создаст два необходимых файла JSON внутри вашего проекта.

Примечание. Если вы этого не сделали, загрузите и установите Node.JS и NPM перед выполнением любых команд.

Затем мы установим наши любимые три зависимости:

1npm install axios cheerio objects``-``to``-``csv

Оттуда создайте новое имя файла glassdoorScraper.js и импортируйте зависимости вверху:

1const axios = require(``"axios"``);
2const cheerio = require(``"cheerio"``);
3const ObjectsToCsv = require(``"objects-to-csv"``);

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

2. Понимание страниц Glassdoor

При переходе по ссылке «Offerte di lavoro Part time a Milano».

Веб-сайт приведет вас к списку вакансий в виде карточек слева и более подробной информации справа. Каждая карточка представляет работу и содержит всю информацию, которую мы ищем: название компании, должность и ссылку на объявление о вакансии.

Но нас не интересует визуальный рендеринг, не так ли? Чтобы найти цели CSS, давайте изучим страницу и посмотрим, как устроены эти карточки.

Первый элемент на карточке — это название компании, которое находится на три уровня ниже внутри своего контейнера: div > a > span. Это важно заметить, потому что <span>, содержащий текст, не имеет какого-либо атрибута, на который мы могли бы ориентироваться.

Если мы просто выберем <span> внутри нашего синтаксического анализатора, мы извлечем все <span> элементов на странице — это не очень хорошо.

Вместо этого мы можем подняться на уровень выше и настроить таргетинг на родительский тег <a>, потому что у него есть много атрибутов.

Селектор названия компании будет выглядеть примерно так: «a.job-search-key-l2wjgv.e1n63ojh0.jobLink > span».

Другими словами, мы нацеливаемся на каждый тег <a> с атрибутом класса «job-search-key-l2wjgv.e1n63ojh0.jobLink», а затем переходим к дочернему элементу <span>.

Примечание. Этому тегу назначено три разных значения класса; в большинстве случаев эти значения разделяются пробелом при проверке HTML страницы, как на изображении выше. Однако это может стать проблемой при создании селектора в вашем коде, поэтому вместо этого замените эти пробелы точкой.

Если вы проделаете тот же процесс для остальных элементов, это будет выглядеть примерно так:

  • Для названия должности мы на самом деле используем атрибут data-test: «a[data-test=’job-link’] › span»

  • Для URL-адреса мы будем использовать тот же селектор, что и для названия должности, но без элемента <span>.

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

Нет! Прежде чем мы подвергнем опасности наш IP, лучше использовать консоль браузера, чтобы опробовать эти селекторы.

3. Тестирование селекторов внутри консоли браузера

Прямо там, где вы находитесь, нажмите на вкладку Консоль. Там вы увидите много напечатанной информации.

Чтобы избавиться от него, нажмите CTRL + L в ключевом слове, чтобы очистить консоль.

С чистого листа давайте передадим первый селектор в функцию querySelectorAll() и посмотрим, что будет возвращено:

Отлично, это сработало! Как видите, он возвращает в общей сложности 30 узлов, и когда мы наводим на них курсор, они подсвечивают название компании на каждой карточке. Кроме того, теперь мы знаем, что на странице есть 30 вакансий.

Попробуйте протестировать остальные селекторы, чтобы увидеть процесс самостоятельно. Когда вы будете готовы, давайте вернемся к VS Code.

4. Отправка HTTP-запроса через Axios

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

В нашем файле glasssdoorScraper.js давайте создадим новую асинхронную функцию и инициализируем Axios, передав целевой URL.

1(``async function () {
2const page = await axios("");
3})();

Ой! Но мы еще не выбрали URL, не так ли? Возвращаясь к текущей странице, URL-адрес выглядит примерно так:

1[https://www.glassdoor.it/Lavoro/milano-part-time-lavori-SRCH_IL.0](https://www.glassdoor.it/Lavoro/milano-part-time-lavori-SRCH_IL.0),6_IC2802090_KO7,16.htm

Но вы никогда не должны брать первый URL-адрес, не оценив сначала, есть ли лучший вариант.

Например, если мы перейдем к остальным URL-адресам в серии с разбивкой на страницы, вот общая тенденция от страницы к странице:

Страница 2:

https://www.glassdoor.it/Lavoro/milano-part-time-lavori-SRCH_IL.0,6_IC2802090_KO7,16_IP2.htm?includeNoSalaryJobs=true&pgc=AB4AAYEAHgAAAAAAAAAAAAAAAeJsfSYASAEBAQ0BkGkLZy7wZR4%2F2Zo9gFfJc%2BaGfJR2hsdPG88aYkQEq%2BZCuA1D8cX0auxYd5YLWXw4PlrFLs6CbF64VTKidMy%2FVVlQewAA

Страница 3:

https://www.glassdoor.it/Lavoro/milano-part-time-lavori-SRCH_IL.0,6_IC2802090_KO7,16_IP3.htm?includeNoSalaryJobs=true&pgc=AB4AAoEAPAAAAAAAAAAAAAAAAeJsfSYAfQEBARIBKE7V5yzSCE8JHGVBEGqz7wAzuNK9l7EpYDY04B%2FezXcfXZMYR5YqLcFzc7zLnpGx2RjtMQTOQD7v9%2FdGuWhCorhxKDvn80HEk9RcGxMTa110BNJz2wVS1VEBfbcw0u1rpBWrMhZMF1T%2BAHtTQq8aCnkr9ztDMcmkAAA%3D

В этих URL-адресах много шума, но присмотритесь к основе URL-адреса, выделенного желтым цветом.

Если мы используем только эту часть, мы получаем те же результаты, как если бы мы перемещались по нумерации страниц. Итак, давайте использовать эту структуру с этого момента.

1(``async function () {
2const page = await axios(
3"[https://www.glassdoor.it/Lavoro/milano-part-time-lavori-SRCH_IL.0](https://www.glassdoor.it/Lavoro/milano-part-time-lavori-SRCH_IL.0),6_IC2802090_KO7,16_IP1.htm?includeNoSalaryJobs=true"
4);
5
6console.log(page.status);
7})();

И мы ведем консольный журнал для хорошей меры.

Круто, 200 успешных кодов! Однако, прежде чем мы продолжим, нам нужно сделать еще одну вещь, чтобы сделать наш парсер более устойчивым, когда мы масштабируем наш проект для большего количества запросов.

5. Интеграция ScraperAPI, чтобы избежать блокировки

При очистке веб-сайтов с высоким трафиком или большим объемом данных следует учитывать, что большинство из них не любят, когда их очищают, поэтому у них есть несколько хитростей, чтобы заблокировать доступ ваших скриптов к их серверам.

Чтобы обойти это, вам нужно закодировать различные варианты поведения, которые удобны для серверов, на которых ваш парсер на самом деле является реальным человеком, взаимодействующим со страницей, например работа с CAPTCHA, ротация вашего IP-адреса, создание и поддержка пула IP-адресов для ротации, отправка правильных заголовков и изменение вашего IP-адреса для доступа к геочувствительным данным.

Или мы можем использовать простой API для обработки всего этого за нас.

ScraperAPI использует машинное обучение, многолетний статистический анализ и огромные фермы браузеров, чтобы предотвратить пометку и блокировку ваших парсинг-ботов.

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

И мы будем использовать следующую структуру для изменения нашего первоначального запроса:

1http:``/``/``api.scraperapi.com?api_key``=``{yourApiKey}&url``=``https:``/``/``www.glassdoor.it``/``Lavoro``/``milano``-``part``-``time``-``lavori``-``SRCH_IL.``0``,``6_IC2802090_KO7``,``16_IP1``.htm?includeNoSalaryJobs``=``true

Теперь наш запрос будет отправляться с серверов ScraperAPI, чередуя наш IP-адрес в каждом запросе и обрабатывая все сложности и системы защиты от парсинга, с которыми сталкивается наш парсер.

6. Анализ ответа с помощью Cheerio

Самое интересное начинается! Первым шагом к извлечению нужных данных является анализ ответа, чтобы мы могли перемещаться по узлам и выбирать элементы, используя ранее созданные селекторы.

1const html = page.data;
2const $ = cheerio.load(html);

Что вы сделали прямо сейчас, так это сохранили данные ответа (которые являются данными HTML) в переменную, которую вы затем передали Cheerio для анализа.

Cheerio преобразует каждый элемент HTML-файла в объекты Node, которые мы можем перемещать с помощью XPath или, в нашем случае, селекторов CSS.

Тем не менее, есть один селектор, который мы еще не обсуждали: основной контейнер.

На странице каждый список вакансий представлен карточкой, и каждая карточка содержит нужные нам данные. Чтобы облегчить нашему парсеру поиск информации и снизить вероятность утечки бесполезных данных в наш проект, нам сначала нужно выбрать все карты, а затем пройтись по ним, чтобы извлечь точки данных.

Каждая карта является элементом <li>, и мы можем выбрать их, используя атрибут [data-test="jobListing"].

Примечание. Вы не можете увидеть его на изображении из-за выреза на снимке экрана, но вы сможете найти атрибут на странице.

Итак, вот как мы можем написать весь парсер:

1let allJobs = $(``'[data-test="jobListing"]'``);
2allJobs.each((index, element) =``> {
3const jobTitle = $(element).find(``"a[data-test='job-link'] > span"``).text();
4const company = $(element)
5.find(``"a.job-search-key-l2wjgv.e1n63ojh0.jobLink > span"``)
6.text();
7const jobLink = $(element).find(``"a[data-test='job-link']"``).attr(``"href"``);
8});

Обратите внимание на метод .text() в конце строки? Как вы, вероятно, поняли, метод извлекает текстовые данные из элемента. Без него он вернул бы разметку и текст, что не очень полезно.

С другой стороны, когда мы хотим извлечь значение атрибута внутри элемента, мы можем использовать метод .attr() и передать атрибут, из которого мы хотим получить значение.

Если бы мы запустили наш скрипт сейчас, на самом деле ничего не произошло бы, потому что скрипт ничего не делает с данными, которые он собирает.

Мы можем продолжить и записать данные в терминал, но все это будет очень запутанно видеть. Итак, прежде чем мы зарегистрируем его, давайте отформатируем его, используя массив.

7. Перемещение данных в пустой массив

Вне основной асинхронной функции создайте пустой массив следующим образом:

1let jobListings = [];

Чтобы добавить очищенные данные внутрь, все, что нам нужно, это использовать .push() в массиве:

1jobListings.push({
2"Job Title"``: jobTitle,
3"Hiring Company"``: company,
4"Job Link"``: "[https://www.glassdoor.it](https://www.glassdoor.it/)" + jobLink,
5});

Вы уловили это? Мы помещаем строку перед возвращаемым значением из jobLink. Но почему?

Именно поэтому веб-скрапинг касается деталей. Вернемся на страницу и посмотрим значение href:

Там много информации, но в URL отсутствует кусок: https://www.glassdoor.it». Это умный способ защитить URL-адрес от парсеров вроде нас.

Мы объединяем их в одну строку, передавая недостающую информацию в виде строки вместе со значением jobLink. Таким образом, делая его снова полезным.

С этим покончено, давайте протестируем наш код, записав в консоль результирующий массив:

Отличная работа до сих пор; Вы построили самую сложную часть! Теперь давайте вытащим эти данные из терминала, хорошо?

8. Создание CSV-файла

Экспорт очищенной информации в файл CSV на самом деле довольно прост благодаря пакету ObjectsToCsv. Все, что вам нужно сделать, это добавить следующий фрагмент вне метода .each():

1const csv = new ObjectsToCsv(jobListings);
2await csv.toDisk(``"./glassdoorJobs.csv"``, { append: true });
3console.log(``"Save to CSV"``);

Важно, чтобы мы установили append в true, чтобы мы не перезаписывали файл каждый раз, когда мы его используем.

Мы тестировали это раньше, поэтому пока не запускайте свой код. Мы все еще хотим сделать еще одну вещь раньше.

9. Работа с разбитыми на страницы страницами

Мы уже выяснили, как меняется структура URL от страницы к странице в серии с разбивкой на страницы. С помощью этой информации мы можем создать for loop для увеличения номера IP{x}, пока мы не достигнем последней страницы в разбиении на страницы:

1for (let pageNumber = 1``; pageNumber < 31``; pageNumber +``= 1``){}

Кроме того, нам нужно динамически добавить этот номер в запрос axios():

1const page = await axios(
2http://api.scraperapi.com?api_key={yourApiKey}&url=https://www.glassdoor.it/Lavoro/milano-part-time-lavori-SRCH_IL.0,6_IC2802090_KO7,16_IP${pageNumber}.htm?includeNoSalaryJobs=true
3);

Наконец, мы перемещаем весь код внутрь for loop, оставляя часть CSV вне цикла для простоты.

10. Тестовый запуск и полный парсер Glassdoor Node.JS

Если вы внимательно следили (если вы пришли прямо к этому разделу: Привет), ваша кодовая база должна выглядеть так:

1const axios = require(``"axios"``);
2const cheerio = require(``"cheerio"``);
3const ObjectsToCsv = require(``"objects-to-csv"``);
4
5let jobListings = [];
6
7(``async function () {
8for (let pageNumber = 1``; pageNumber < 31``; pageNumber +``= 1``) {
9const page = await axios(
10 `http:```//api.scraperapi.com?api_key=51e43be283e4db2a5afb62660fc6ee44&url=https://www.glassdoor.it/Lavoro/milano-part-time-lavori-SRCH_IL.0_143require(``"axios"``);IC2802090_KO7_144<span>IP${pageNumber}.htm?includeNoSalaryJobs=```true` ``
11);
12const html = await page.data;
13const $ = cheerio.load(html);
14
15let allJobs = $(``'[data-test="jobListing"]'``);
16allJobs.each((index, element) =``> {
17const jobTitle = $(element).find(``"a[data-test='job-link'] > span"``).text();
18const company = $(element).find(``"a.e1n63ojh0 > span"``).text();
19const jobLink = $(element).find(``"a[data-test='job-link']"``).attr(``"href"``);
20jobListings.push({
21"Job Title"``: jobTitle,
22"Hiring Company"``: company,
23"Job Link"``: "[https://www.glassdoor.it/](https://www.glassdoor.it/)" + jobLink,
24});
25});
26
27console.log(pageNumber + " Done!"``);
28}
29
30const csv = new ObjectsToCsv(jobListings);
31await csv.toDisk(``"./glassdoorJobs.csv"``);
32console.log(``"Save to CSV"``);
33console.log(jobListings);
34})();

После запуска вашего кода внутри вашей папки будет создан новый файл CSV.

Примечание. Чтобы это работало, помните, что вам нужно добавить свой ключ ScraperAPI в сценарий, заменив заполнитель {yourApiKey}.

Мы внесли несколько изменений:

  • Во-первых, мы добавили строку console.log(pageNumber + " Done!") для визуальной обратной связи во время работы скрипта.
  • Во-вторых, мы удаляем аргумент { append: true } из метода .toDisk(); поскольку он больше не находится внутри for loop, мы не будем добавлять (добавлять) к нему больше данных.

Поздравляем, вы создали свой первый парсер Glassdoor на JavaScript!

Вы можете использовать те же принципы для очистки практически каждой страницы на Glassdoor и, используя ту же логику, вы можете перевести этот скрипт на другие языки.

Давайте создадим скрипт Python, чтобы сделать то же самое, что и для демонстрации.

Парсинг заданий Glassdoor в Python

При написании парсера Glassdoor на Python вы можете использовать такой инструмент, как Selenium. Однако, как и в случае с JavaScript, нам не нужно использовать какой-либо безголовый браузер.

Вместо этого мы будем использовать Requests и Beautiful Soup для создания цикла для доступа и анализа HTML страниц с разбивкой на страницы, извлекая данные, как мы делали выше.

1. Настройка среды Python

Внутри папки вашего проекта создайте новый каталог glassdoor-python-scraper и добавьте файл glassdoor_scraper.py, а затем pip установите Requests и Beautiful Soup из терминала:

1pip install requests beautifulsoup4

Наконец, импортируйте обе зависимости в начало файла:

1import requests

2from bs4 import BeautifulSoup

Вот так мы готовы к следующему шагу.

2. Использование запросов в цикле for

На всякий случай отправьте первоначальный запрос на сервер и распечатайте код состояния.

1response = requests.get(
2"[https://www.glassdoor.it/Lavoro/milano-part-time-lavori-SRCH_IL.0](https://www.glassdoor.it/Lavoro/milano-part-time-lavori-SRCH_IL.0),6_IC2802090_KO7,16_IP1.htm?includeNoSalaryJobs=true"``)
3
4print``(response.status_code)

Примечание. Помните, что вам потребуется компакт-диск в новую папку, прежде чем вы сможете запустить свой скрипт Python.

Это работает до сих пор! Теперь давайте поместим это в for loop и попробуем получить доступ к первым трем страницам в разбиении на страницы. Для этого мы создадим диапазон от 1 до 4 (он не будет включать 4 в диапазон) и добавим в строку переменную {x}:

1for x in range``(``1``, 4``):
2response = requests.get(
3"[https://www.glassdoor.it/Lavoro/milano-part-time-lavori-SRCH_IL.0](https://www.glassdoor.it/Lavoro/milano-part-time-lavori-SRCH_IL.0),6_IC2802090_KO7,16_IP{x}.htm?includeNoSalaryJobs=true"``)
4
5print``(response.status_code)

С помощью этого простого цикла for наш парсер сможет перемещаться по разбивке на страницы без каких-либо проблем.

3. Парсинг данных Glassdoor с помощью Beautiful Soup

В целях тестирования мы не хотим, чтобы наш парсер давал сбои на трех разных страницах, поэтому давайте уменьшим диапазон до 1–2; он будет очищать только первую страницу.

Как и прежде, мы выберем все карты заданий с помощью селектора атрибутов [data-test="jobListing"]:

1all_jobs = soup.select(``"[data-test='jobListing']"``)

Со всеми картами, хранящимися внутри переменной all_jobs, мы можем просмотреть их, чтобы извлечь целевые точки данных:

1for job in all_jobs:
2job_title = job.find(``"a"``, attrs``=``{``"data-test"``: "job-link"``}).text
3company = job.select_one(
4"a.job-search-key-l2wjgv.e1n63ojh0.jobLink > span"``).text
5job_link = job.find(``"a"``, attrs``=``{``"data-test"``: "job-link"``})[``"href"``]

Примечание. По какой-то причине using .find() для извлечения названия компании не работало, поэтому вместо этого мы решили использовать метод select_one().

4. Создание файла JSON

Мы более подробно рассказали об обработке файлов JSON в нашем учебнике Очистка табличных данных с помощью Python. Тем не менее, для краткого объяснения, мы добавим данные в пустой массив и воспользуемся методом json.dump() для сохранения массива в файл JSON:

1glassdoor_jobs.append({
2"Job Title"``: job_title,
3"Company"``: company,
4"Job Link"``: "[https://www.glassdoor.it](https://www.glassdoor.it/)" + job_link
5})

Примечание. Чтобы это работало, вам потребуется импортировать json в начало файла и создать новый пустой массив glassdoor_jobs = [] вне цикла.

Когда массив готов с нашими данными в удобном формате, мы выгрузим данные в файл JSON со следующим фрагментом:

1with open``(``'glassdoor_jobs'``, 'w'``) as json_file:
2json.dump(glassdoor_jobs, json_file, indent``=``2``)

Последнее, что нужно сделать: протестировать!

5. Тестовый прогон и полный скребок Glassdoor Python

Без лишних преамбул, вот полный скрипт Python для очистки данных о заданиях Glassdoor:

1import requests
2from bs4 import BeautifulSoup
3import json
4
5glassdoor_jobs = []
6
7for x in range``(``1``, 31``):
8response = requests.get(
9"[http://api.scraperapi.com?api_key=](http://api.scraperapi.com/?api_key=){your_api_key}&url=[https://www.glassdoor.it/Lavoro/milano-part-time-lavori-SRCH_IL.0](https://www.glassdoor.it/Lavoro/milano-part-time-lavori-SRCH_IL.0),6_IC2802090_KO7,16_IP{x}.htm?includeNoSalaryJobs=true"``)
10soup = BeautifulSoup(response.content, "html.parser"``)
11
12all_jobs = soup.select(``"[data-test='jobListing']"``)
13for job in all_jobs:
14job_title = job.find(``"a"``, attrs``=``{``"data-test"``: "job-link"``}).text
15company = job.select_one(
16"a.job-search-key-l2wjgv.e1n63ojh0.jobLink > span"``).text
17job_link = job.find(``"a"``, attrs``=``{``"data-test"``: "job-link"``})[``"href"``]
18glassdoor_jobs.append({
19"Job Title"``: job_title,
20"Company"``: company,
21"Job Link"``: "[https://www.glassdoor.it](https://www.glassdoor.it/)" + job_link
22})
23print``(``"Page " + str``(x) + " is done"``)
24
25with open``(``'glassdoor_jobs'``, 'w'``) as json_file:
26json.dump(glassdoor_jobs, json_file, indent``=``2``)

Несколько изменений, которые мы внесли:

  • Мы изменили диапазон с 1–2 на 1–31. Сценарий остановится на 30-й странице (поскольку 31-я не включена), которая является последней страницей в разбивке на страницы.
  • Мы добавили оператор print("Page: " + str(x) + " is done") для визуальной обратной связи во время выполнения кода. Он преобразует нашу переменную x из целого числа в строку, чтобы мы могли объединить всю фразу.
  • Чтобы защитить наш IP-адрес и справиться с любым методом защиты от парсинга, мы будем отправлять наши запросы через серверы ScraperAPI. Вы можете увидеть новую строку в исходном URL-адресе и узнать больше о функциях ScraperAPI в нашей документации.

Вот конечный результат:

Собрано 30 страниц, и все данные отформатированы в многоразовый файл JSON.

Подведение итогов

Масштабируя этот проект, вы можете очищать больше страниц и получать еще больше точек данных. Вы также можете собирать информацию о конкретных вакансиях, отфильтровывая информацию, например, только о вакансиях с определенным названием, местоположением или ценностью (т.

С таким большим количеством информации небо — это предел, так что держите свой разум открытым для возможностей.

До следующего раза, счастливого соскабливания!

Дополнительные материалы на PlainEnglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter, LinkedIn, YouTube и Discord . Заинтересованы в хакинге роста? Ознакомьтесь с разделом Схема.