Веб-скрапинг — это процесс сбора информации из Интернета для автоматизации громоздких задач. Библиотеки Python, запросы и Beautiful Soup — мощные инструменты для извлечения данных из Интернета. В этом уроке мы создадим веб-скрейпер, который извлекает списки вакансий аналитика данных с действительного веб-сайта. Для этого наш парсер будет анализировать HTML-код на сайте, чтобы собрать соответствующую информацию.

Весь код, относящийся к этому руководству, можно найти здесь.

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

Краткое введение в HTML и Beautiful Soup

Прежде чем углубляться в конкретные детали извлечения данных из Действительно, мы сначала узнаем об основах HTML. Как правило, нам нужно очистить часть веб-сайта, написанную на HTML. Базовое понимание HTML поможет нам определить HTML-код, который необходимо удалить.

Beautiful Soup — это библиотека Python, используемая для извлечения информации из файлов HTML и XML. В этом руководстве мы будем использовать анализатор HTML для чтения содержимого веб-сайта Indeed, написанного на HTML. Базовое введение в Beautiful Soup поможет нам понять древовидную структуру объектов Python в Beautiful Soup. Во-первых, давайте начнем с основ HTML.

Основы HTML

Язык разметки гипертекста, или сокращенно HTML, представляет собой систему форматирования для отображения материалов, полученных через Интернет. Используя этот язык, пользователи могут создавать и структурировать разделы, абзацы и ссылки, используя элементы, теги и атрибуты. Веб-браузер, такой как Chrome, считывает эти элементы, теги и атрибуты для отображения содержимого документа на понятном человеку языке (английском). Простая структура HTML-страницы представлена ​​на рисунке ниже.

Давайте скопируем этот код в блокнот и сохраним файл блокнота как page.html. Когда мы открываем файл page.html в Google Chrome, он выглядит так.

Хорошо, но что такое тег, элемент и атрибут в HTML.

Тег.Тег — это скрытое ключевое слово на веб-странице, которое определяет, как веб-браузер должен форматировать и отображать содержимое. В HTML есть теги для заголовков, абзацев, ссылок, таблиц, заголовков и т. д. Тег содержит свое имя в угловых скобках и может быть парным, что составляет начальный и конечный теги, определяющие конкретный фрагмент кода, текст или другие элементы. теги. На рисунке выше каждое ключевое слово, содержащееся в ‹›, является тегом и имеет определенную цель. Например, тег ‹h1› используется для определения заголовков первого уровня в HTML.

Элемент. HTML-элемент — это все, что находится между начальным и конечным тегами. На рисунке выше «‹h1› Заголовок 1 ‹/h1›» является элементом.

Атрибут. Элемент в HTML может иметь атрибуты для предоставления дополнительной информации о нем. Атрибуты всегда определяются в начальном теге и обычно представляют собой пары имя/значение. Например, в последнем коде можно использовать атрибут стиля с тегом ‹p›, чтобы придать этому элементу дополнительные свойства. Строка ниже напечатает текст пункта 1 синим цветом.

‹p style=‘color:blue;’ › Параграф 1 ‹/p›

Двумя наиболее важными атрибутами, которые пригодятся при парсинге веб-контента, являются class и id.

Допустим, мы хотим иметь один и тот же стиль для нескольких элементов (в этом примере один и тот же цвет текста для нескольких абзацев). Это можно сделать, либо поместив атрибут стиля несколько раз для каждого из элементов, либо определив класс в начале и используя его в качестве атрибута для всех элементов. Последний способ более эффективен. Таким образом, один и тот же класс style может использоваться как для абзаца 1, так и для абзаца 2 следующим образом:

‹p class=‘style’ › Параграф 1 ‹/p›

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

Основы красивого супа

Чтобы понять основные методы и объекты Beautiful Soup, давайте воспользуемся HTML-кодом, показанным на первом рисунке. Я добавил несколько классов и ссылок в этот код, чтобы лучше понять процесс синтаксического анализа и создания объектов в Beautiful Soup. Наш окончательный HTML-код выглядит так и присваивается переменной html_code.

html_code = """
<html>
<head>
<title>Page Title</title>
<head>
<body>
<h1> Heading 1 </h1>
<p class="style"> Paragraph 1 </p>
<p class="style"> Paragraph 2 </p>
<a href="https://www.google.com/" class="Alphabet" id="link1"> Google </a>
<a href="https://www.google.com/doodles" class="Alphabet" id="link2"> Doodle </a>
</body>
</html>"""

Теперь давайте проанализируем этот HTML-код с помощью HTML-парсера Beautiful Soup.

soup = bs(html_code, "html.parser")

Суп-объект — это дерево сложных объектов Python. Здесь описаны некоторые из важных объектов, которые используются при очистке веб-страниц.

Тег. Объект тега соответствует тегу HTML в документе. Этот объект является атрибутом (потомком) объекта супа и содержит элемент, связанный с тегом HTML.

soup = bs(r'<p class="style">Paragraph 1</p>', "html.parser")
tag = soup.p #the element with html tag p
print(tag)
print(type(tag)) #tag object of Beautiful Soup

выход

<p class="style">Paragraph 1</p>
<class 'bs4.element.Tag'>

Тег может содержать другие теги (элементы). Эти элементы называются дочерними элементами тега. Мы можем перемещаться по этим дочерним элементам, используя цепочку атрибутов.

soup = bs(html_code, "html.parser")
soup.body.h1.p
<p class="style">Paragraph 1</p> #children of object h1

Атрибуты. Тег может иметь несколько атрибутов. Эти атрибуты могут быть атрибутами с одним значением (например, атрибут id в HTML) или многозначными атрибутами (например, атрибут класса в HTML). Вы можете получить доступ к атрибутам тега, рассматривая его как словарь. Значения многозначных атрибутов возвращаются в виде списка.

print(soup.a['id'])
print(soup.a['class'])
link1
['Alphabet']

Из этих примеров видно, что мы можем получить доступ только к одному HTML-тегу, используя метод .attribute. Чтобы получить все теги с одинаковыми именами, мы можем использовать функцию find_all из Beautiful Soup. В приведенном ниже примере функция find_all возвращает список тегов с именем a.

soup.find_all('a')
[<a class="Alphabet" href="https://www.google.com/" id="link1">Google</a>,
 <a class="Alphabet" href="https://www.google.com/doodles" id="link2">Doodle</a>]

Мы можем передавать различные аргументы функции find_all для выбора определенных тегов.

#a tag with ref google.com
print(soup.find_all('a', attrs={'href':'https://www.google.com/'}))
#a tag with id link2
print(soup.find_all('a', attrs={'id':'link2'}))
#p tag with class style
print(soup.find_all('p', attrs={'class':'style'}))

[<a class="Alphabet" href="https://www.google.com/" id="link1">Google</a>]
[<a class="Alphabet" href="https://www.google.com/doodles" id="link2">Doodle</a>]
[<p class="style">Paragraph 1</p>, <p class="style">Paragraph 2</p>]

Теперь давайте воспользуемся Beautiful Soup для извлечения данных о вакансиях из Indeed.

Сбор списков вакансий аналитика данных с сайта Indeed:

Во-первых, мы должны понимать структуру веб-сайта, чтобы собирать данные, которые имеют отношение к нам. На сайте Indeed мы видим пользовательский интерфейс, как показано на рисунке ниже. Кроме того, мы видим, что когда мы взаимодействуем с веб-сайтом, URL-адрес веб-сайта меняется. Для этого урока я ищу списки вакансий аналитика данных в городе Торонто в радиусе 100 км.

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

URL = 'https://ca.indeed.com/jobs?q=%22data%20Analyst%22&l=Toronto%2C%20ON&radius=100&vjk=b1e75865f696b55e'
page = req.get(URL) 

Вы можете проверить содержимое этой страницы, используя page.text. Когда мы просматриваем эту страницу, мы видим на рисунке ниже, что часть карточки вакансии с информацией (название компании, должность, местоположение и зарплата) находится внутри тега «td» и класса «resultContent». Давайте сохраним данные с этой карты в списке с именем res. В этом списке хранятся данные обо всех заданиях.

res = soup.find_all('td', 'resultContent')

Теперь давайте найдем часть HTML-кода, представляющую название должности для первой работы. Когда мы наводим указатель мыши на название должности и щелкаем правой кнопкой мыши для проверки, мы видим, что название должности расположено в виде строки в теге span, который является дочерним элементом тега h2. Кроме того, тег h2 является потомком тега td.

res[0].find('h2').text
'newData Analyst'

Здесь res[0] дает нам информацию о должности, компании, местоположении и зарплате. Часть res[0].find(‘h2’) дает нам элемент, связанный с тегом h2. Наконец, res[0].find(‘h2’).text дает нам строковую часть элемента. Строка состоит из двух частей: new и job title. Мы можем удалить из него «новое», используя функцию замены.

res[0].find('h2').text.replace('new','')
'Data Analyst'

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

print(res[0].find('h2').text.replace('new','')) #job title
print(res[0].pre.span.string) #company name
print(res[5].find('div', 'salary-snippet').get('aria-label')) #salary 
print(res[0].find('div', 'companyLocation').text) #job location
print(soup.find_all('span', 'date')[0].text) #job posting date
Data Analyst
Loblaw Companies Limited
$22.65 to $24.56 an hour
Brampton, ON
Posted 1 day ago

Здесь обратите внимание, что я использовал res[5] для получения зарплаты, потому что в первой карточке зарплата не упоминается, поэтому я использовал шестую карточку. Кроме того, дата публикации вакансии находится внизу карточки, поэтому я использовал объект супа, чтобы найти все теги span, имеющие класс даты.

Теперь у нас есть представление о том, как получить необходимую информацию для одной карты. Давайте получим информацию обо всех картах на одной странице. Для этого мы можем перебрать все элементы списка res, содержащего информацию о карточках, на одной странице. Для этого реализована функция one_page().

#store the result of each variable for every job on a page in a list
job_title = []
company = []
location = []
salary = []
date = []
def one_page(res, job_title, company, location, salary, date):
    for element in res:
        job_title.append(element.find('h2').text.replace('new',''))
        company.append(element.pre.span.string)
        location.append(element.find('div', 'companyLocation').text)
        
        #use try and except statment to get the salary. If salary is
         #not mentioned, store it as an empty string
        try:
            salary.append(element.find('div', 'salary
                          snippet').get('aria-label'))        
        except AttributeError:
            salary.append('')
    #loop through all the cards to get posted date
    for dat in soup.find_all('span', 'date'):
        date.append(dat.text)
    return (job_title, company, location, salary, date)

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

#let's collect data for all the pages (i am collecting data for #first 20 pages). If you want to collect data for all the pages you #can use while loop instead of for loop.
for i in range(20):
    #data for first page
    one_page(res, job_title, company, location, salary, date)
    
    #use a try statment to get the url of next page
    try:
        url = 'https://ca.indeed.com' + soup.find_all('a', {'aria-  
                                     label':'Next'})[0].get('href')
    except:
        break
    #based on the url for each page update vlaues of page, soup, res
    page = req.get(url)
    soup = bs(page.text, "html.parser")
    res = soup.find_all('td', 'resultContent')

Сохраните эти данные во фрейме данных для дальнейшего анализа. Кроме того, вы можете экспортировать его в файл CSV для последующего использования.

df_jobs = pd.DataFrame(data={'Job_title':job_title,
                       'Company':company, 'Location':location, 
                        'Salary':salary, 'Date':date})
df_jobs.to_csv(r'address\job_data.csv')
df_jobs.head()

Заключение

В этом уроке мы изучили основы HTML и Beautiful Soup для парсинга веб-страниц. Мы использовали Beautiful Soup для сбора списков вакансий, связанных с ролью аналитика данных в Торонто, с сайта Indeed. Здесь я собрал данные только по региону Торонто, вы можете определить местоположение как переменную в URL-адресе, чтобы получить списки вакансий для разных городов. Наконец, данные экспортируются в файл CSV, который можно использовать позже для анализа.

Надеюсь, вам понравился этот урок. Не стесняйтесь комментировать с вашими вопросами и предложениями :)