CoderCastrov logo
CoderCastrov
Парсер

От пауков к R и обратно...

От пауков к R и обратно...
просмотров
13 мин чтение
#Парсер

Проект по текстовому анализу для социальных ученых (которые любят компьютеры, но не знают, с чего начать)

Часть 1 — Парсинг веб-страниц с помощью Python

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

Правда, я расскажу вам о крайне узкой области исследования: группы крайне левых в Великобритании, которые поддерживают правительство Мадуро в Венесуэле. Действительно узкая тема. Чтобы понять контекст ситуации в Венесуэле в настоящее время (и сделать собственные выводы), я рекомендую вам посмотреть этот документальный фильм на iPlayer BBC. Я воздержусь от выражения своего мнения по этому вопросу - скажу только, что это слишком близко к сердцу, и обсуждение может занять много времени (но если вам интересно, вы можете спросить меня об этом, так как я пишу об этом уже 4 года!)

В этом анализе я сосредоточусь на онлайн-активности этих групп в Великобритании: конкретно я рассмотрю их более обширную письменную активность - вот почему методы текстового анализа и анализа текста подходят. В этом учебнике я сосредоточусь только на одной конкретной группе в Великобритании - кампании "Hands off Venezuela" (HOV). (На самом деле в Великобритании есть несколько групп, поддерживающих Мадуро, если вам интересно. Странно, я знаю.) Я также сосредоточусь только на их блогах, в отличие от их активности на Facebook или Twitter, о которых я расскажу в другом проекте.

Наша первая задача - собрать всю письменную активность HOV вместе в прекрасной таблице (о том, как должна выглядеть эта таблица, мы поговорим в части 2). Один из способов собрать наши данные, или любые текстовые данные, заключается в копировании и вставке непосредственно. Знаете, с помощью мыши и веб-браузера. Но, я настоятельно утверждаю, что это ужасная идея. Это не только занимает много времени, вызывает скуку и подвержено ошибкам, но и не является воспроизводимым. И как стремящийся стать ученым-исследователем данных, я должен сказать вам, что невоспроизводимость - это полный неприемлемо (опять же, я с удовольствием расскажу вам, почему, если вы попросите вежливо). Конечно, иногда у вас не будет выбора. Но давайте попробуем.

Сначала нам нужно научиться «скрапить» эту информацию с блога HOV (или с Интернета в целом). Другими словами, нам нужно научить наш компьютер копировать и вставлять за нас с помощью набора инструкций. Как вы спросите? Введите Scrapy: удобное приложение, написанное на Python, которое мы можем использовать для выполнения этой задачи (см. документацию Scrapy здесь.)

Я дам представление о том, что может делать Scrapy, и если вы не слишком запуганы консолью (или терминалом) вашего компьютера, вы можете следовать за мной, набирая команды, как они показаны ниже. Фактический паук, созданный для парсинга сайта HOV, находится в моем репозитории на GitHub. Здесь вы также можете скачать файл, который я использовал для проведения текстового анализа, и следовать более продвинутым инструкциям (вам повезло! Вы можете пропустить этот учебник полностью!)

Я разделю этот пост на 5 разделов:

Некоторые юридические моменты: условия использования веб-сайта указывают политику парсинга. Некоторые сайты явно запрещают парсинг (например, IMDB), а другие - менее явно. Данные, которые мы пытаемся получить, увеличивают нагрузку на сервер, поэтому у компаний есть причины ограничивать парсинг. Документ robots.txt веб-сайта должен четко это описывать. Вы можете получить доступ к этой информации, добавив к имени веб-сайта слэш и набрав /robots.txt.

Пока давайте создадим простого паука, который научит нас основам парсинга, эмм... законным образом. Если вы хотите узнать, как работать с текстовыми данными, которые у вас уже есть, пропустите эту часть по парсингу веб-страниц на Python и перейдите к части 2 и 3, где я расскажу, как делать текстовый анализ в R.


1. Установка Scrapy

Для установки Scrapy откройте консоль или терминал и следуйте инструкциям для вашей операционной системы.

Для тех, кто новичок во всем этом, отмечу, что все, что идет после символа решетки, является "комментарием" и не интерпретируется консолью - то есть предназначено только для человеческих глаз. Ваша консоль - это приложение Terminal на Mac, Command (cmd) на Windows и Linux Shell (или терминал).

Windows

Здесь мы убеждаемся, что 1) наш pip обновлен, 2) устанавливаем зависимости (в данном случае 'pypiwin32'), и только после этого устанавливаем Scrapy:

# Установка pip
python -m pip install --upgrade pip

# Установка зависимостей
pip install pypiwin32

# Установка Scrapy
pip install scrapy

Linux

Здесь мы устанавливаем зависимости, затем убеждаемся, что pip обновлен, и только после этого устанавливаем Scrapy:

# Установка зависимостей
sudo apt-get install python3 python3-dev python-pip libxml2-dev libxslt1-dev zlib1g-dev libffi-dev libssl-dev

# Обновление pip
pip install --upgrade pip

# Установка scrapy
pip install scrapy

Mac

Здесь мы обновляем Xcode, затем убеждаемся, что home-brew и python обновлены, и только после этого устанавливаем Scrapy (Обратите внимание, что в зависимости от версии Python (2, 3) вы должны использовать pip3 вместо pip ниже).

# Обновление Xcode
xcode-select --install# Установка Homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"# Обновление переменной PATH, чтобы Homebrew запускался перед системными пакетами
echo "export PATH=/usr/local/bin:/usr/local/sbin:$PATH" >> ~/.bashrcsource ~/.bashrc# Убедитесь, что Homebrew и Python обновлены
brew update; brew upgrade Python# Установка Scrapy
pip install scrapy

2. Наш первый паук!

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

scrapy startproject wikiNobel

Это создаст группу файлов, которые нам понадобятся для запуска нашего паука. Я особенно боюсь настоящих пауков, поэтому даже написание и чтение этого слова не приносит мне удовольствия (я визуальное существо). Тот факт, что я пишу так много о пауках, несмотря на то, как я отношусь к настоящим, говорит о том, насколько полезными они являются в виде программного обеспечения. Они удивительны!

Теперь у вас должна быть группа папок, которая выглядит так:

wikiNobel
├── scrapy.cfg          --> файл конфигурации проекта scrapy
└── wikiNobel           --> это ваш модуль проекта Scrapy
    ├── __init__.py     --> инициализирует модуль
    ├── items.py        --> файл определения элементов проекта
    ├── middlewares.py  --> файл промежуточного программного обеспечения проекта
    ├── pipelines.py    --> файл конвейера проекта
    ├── settings.py     --> файл настроек проекта
    └── spiders         --> сохраните здесь файл flaurates.py!
        ├── __init__.py         
        ├── flaurates.py

Теперь мы собираемся создать новый скрипт на языке Python в папке /wikiNobel/spiders и назвать его flaureates.py. Если вы никогда не создавали скрипт на языке Python, не беспокойтесь, вам не нужно знать язык в совершенстве, введение здесь должно быть достаточным для написания вашего паука. Вы можете ознакомиться с известным руководством Ала Свейгарта в качестве введения в язык. Это очень весело, я обещаю, и вы даже научитесь писать свой собственный хранитель паролей. Если вы хотите попробовать что-то более интерактивное, мне очень понравилось введение Socratica на YouTube.

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

Чтобы создать этот скрипт на языке Python, нам понадобится текстовый редактор. Мы можем написать его в любом текстовом редакторе - даже вашем надежном TextEdit. Но есть и другие прекрасные редакторы кода, некоторые из которых подсвечивают код и даже предлагают варианты (подобно предложениям при вводе на вашем смартфоне). Я лично очень люблю Atom, но пожалуйста, попробуйте тот, который вам больше нравится.

После открытия редактора введите следующее:

Понимание файла Spider

Хорошо. Давай разберемся с этим...

Во-первых, import scrapy говорит Python'у обратиться к скрипту Scrapy и, следовательно, к его функциям и классам.

Во-вторых, внутри class мы создаем подкласс класса скрапера, который уже предоставляет Scrapy (scrapy.Spider). Мы называем его wikiNobel. Имя важно, потому что мы будем использовать это имя для вызова этого конкретного скрапера из командной строки.

Затем мы устанавливаем ограничения на веб-страницы, которые скрапер может посещать (в данном случае, только wiki):

allowed_domains перечисляет домены, которые скрапер может парсить

start_urls перечисляет URL-адрес, с которого скраперу разрешено начать свой 'поиск', то есть это первый URL-адрес, который скрапер прочитает.

Давай попрактикуемся в извлечении заголовка страницы (пока что). Напиши функцию def, с отступом на второй строке:

data = {} определяет пустой словарь, в котором Scrapy будет сохранять наши извлеченные заголовки. Это должно иметь отступ вместе со всем, что находится ниже! parse - это основная функция скрапера. Как заметка, не меняйте имя этой функции, иначе скрапер не будет работать!

response.xpath(‘//h1[[@class](http://twitter.com/class)=”firstHeading”]/text()’).extract()— в очень (очень) грубых терминах означает:

ПРИМЕЧАНИЕ: Убедитесь, что вы сохраняете свой скрипт Python в каталоге /wikinobel/spiders/ и называете его flaureates.py - если вы этого не сделаете, Scrapy не сможет его найти!

Запуск нашего паука

Давайте запустим паука! Перейдите в консоль (убедитесь, что вы все еще находитесь в директории wikiNobel/wikiNobel) и введите:

scrapy crawl wikiNobel

Теперь вы увидите много текста, который выводится в консоль (страшно, не так ли??)

Если вы просмотрите информацию, которую Scrapy отправляет — конечно же, с терпением — вы должны увидеть где-то в середине этого леса текста что-то вроде этого (если все прошло гладко). Дата, конечно, будет отличаться:

2020-11-19 11:18:52 [scrapy.core.scraper] DEBUG: Scraped from <200 https://en.wikipedia.org/wiki/List_of_female_Nobel_laureates>
{'title': ['List of female Nobel laureates - Wikipedia']}

Бинго! Вы создали своего первого паука, который извлекает заголовок страницы Википедии о женщинах-лауреатах Нобелевской премии! Пока не очень полезно, признаюсь. Мы к этому дойдем.

3. Кодирование немного более сложного парсера

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

Такой парсер должен:

Шаг 1: Получить все вики-страницы

Вернитесь в свой редактор и замените раздел def parse(self, response): на новые строки кода, которые показаны ниже.

NOTE: Не используйте пробелы для создания отступов, иначе Python не сможет правильно их прочитать! При копировании/вставке текстовые редакторы иногда преобразуют отступы в пробелы, и вы никогда не узнаете, что происходит. Код выглядит точно так же. Лучшая практика - набирать код самостоятельно. Поверьте мне. Я был там, и это было неприятно.

Как вы можете видеть, xpath может стать довольно сложным!

Давайте взглянем на нашу первую функцию parse, которая является значительным шагом вперед по сравнению с предыдущей.

Здесь response.xpath('//span[@class="vcard"]//a/@href).extract() ищет все элементы <span> в HTML, у которых vcard указан как их class, то есть [@class="vcard"]

XPath указывает на элемент <a> (внутри этого раздела <span>), который также содержит href. В HTML есть много href, но в данном случае мы ищем те, которые содержат ссылки на вики-страницы лауреатов. Они случайно появляются под классом vcardтолько под этим классом span).

Поскольку href указан в сокращенной форме (например, как /wiki/Bertha_von_Suttner, а не в абсолютной форме (т.е. как https://en.wikipedia.org/wiki/Bertha_von_Suttner), мы должны попросить Scrapy пройти через список href, извлеченных из страницы, и выполнить urljoin. Эта удобная функция объединяет сокращенную форму с полным или абсолютным адресом. (Помните, когда мы указывали allowed_domains для нашего парсера? Теперь это пригодится.)

Мы просим Scrapy вывести URL на экран, чтобы мы могли увидеть его, когда запустим парсер, а затем просим его выполнить свою вторую задачу дня: функцию parse_bday, которую мы определяем ниже.

Шаг 2: Получение информации из каждой статьи на Википедии

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

Снова обратите внимание на отступы здесь!

Вот наш код:

import scrapy

class NobelSpider(scrapy.Spider):
    name = "wikiNobel"
    start_urls = [
        'https://en.wikipedia.org/wiki/List_of_Nobel_laureates'
    ]

    def parse(self, response):
        for laureate in response.css('tr td:nth-child(2)'):
            link = laureate.css('a::attr(href)').get()
            yield response.follow(link, self.parse_bday)

    def parse_bday(self, response):
        name = response.css('h1::text').get()
        bday = response.css('.bday::text').get()
        category = response.css('.mw-parser-output > ul:nth-child(6) > li:nth-child(1) > a::text').get()
        yield {
            'name': name,
            'birthdate': bday,
            'category': category
        }

Ого! Хорошо, давайте разберемся...

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

Для каждой ссылки, которую паук извлек из основных лауреатов, ему нужно:

В случае, если вы задаетесь вопросом... Мы извлекаем его из элемента <a> (а не из всего текста), потому что он содержит ссылку на статью на Википедии о конкретной категории. В теории это должно гарантировать, что мы не будем просто извлекать слово "Нобелевская" отдельно, если была ссылка на самого Альфреда Нобеля, тогда нам бы пришлось проверить. Действительно, вы увидите, что это произошло с одним лауреатом, Ритой Леви-Монтальчини, так как каждая статья на Википедии кодируется по-разному. К сожалению... вам нужно исправить это вручную.

Теперь мы возвращаемся в консоль и снова запускаем паука. На этот раз мы собираемся сохранить вывод в файл .csv.

scrapy crawl wikiNobel -o nobels.csv

4. Инструменты для тестирования xpath

Селекторы xpath могут быть довольно сложными. Мы только немного коснулись этой темы. В общем, нам нужно детально изучить HTML-код, чтобы понять, что мы хотим извлечь с помощью нашего парсера, поэтому хорошее понимание того, как HTML построен, полезно. Пока что важно, чтобы вы думали о структуре тегов: <h1>, <h2> и т.д. обозначают заголовки; ссылки (href) обычно находятся под тегом <a>; тексты обычно находятся под тегом <p>, и т.д. Для более подробной информации о HTML и его структуре ознакомьтесь с этим курсом о основах.

Мы можем тестировать xpath, открывая инструменты разработчика Chrome. Мы выбираем часть веб-страницы, которую хотим извлечь, щелкаем правой кнопкой мыши на ней и выбираем "Inspect" из доступных опций. Это откроет весь HTML-код (вы увидите его сбоку). Когда у нас есть весь HTML рядом с самой страницей, нажмите ⌘F (command + F на Mac, control + F на Windows). Это откроет поисковый инструмент, в котором мы можем вводить наши xpath. Chrome будет подсвечивать то, что он находит в HTML-документе (см. изображение ниже).

Поисковый инструмент в инструментах разработчика Chrome

В этом примере вы можете видеть, что Chrome нашел 58 имен с использованием данного xpath, в данном случае //span[@class="vcard"]//a/text().

Вы можете нажимать стрелки, чтобы перемещаться по всем найденным экземплярам. В предоставленном примере Chrome подсвечивает второй экземпляр всех найденных имен, в данном случае это Берта фон Зуттнер (лауреат 2 из 58). Это должно означать, что всего есть 57 лауреатов - Мария Кюри дважды получила Нобелевскую премию и является единственным человеком, который получил ее в двух разных областях! (Говорят о многозадачности!! Какая легенда!!) Хорошей практикой является проверка количества экземпляров, которые возвращает ваш xpath, чтобы убедиться, что он получает то, что вам нужно, и не больше (возможно, вам придется использовать extract_first, например).

Не отчаивайтесь, если у вас возникнут трудности с xpath! Кривая обучения довольно крутая.

Scrapy включает свою собственную "оболочку", то есть свой собственный терминал, в котором вы можете непосредственно вводить команды scrapy. Оболочка помогает нам изучать response во время написания наших селекторов; другими словами, мы можем ввести response.xpath('//h1/text()').extract() и это вернет то, что он находит (если вообще находит что-то, прямо сейчас и там. Таким образом, мы убеждаемся, что Scrapy выбирает информацию, которую мы хотим, прежде чем "закрепить" ее в нашем текстовом редакторе.

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

5. Еще более сложный парсер...

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

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

Если вы совсем новичок в программировании, вы поймете, что существует ОЧЕНЬ много информации, которая поможет вам справиться с ошибками, устранением неполадок и общими проблемами новичков. Пожалуйста, почувствуйте себя в хорошей компании. Сообщество программирования и Data Science чрезвычайно щедры на свое время, и вы можете найти ответы на большинство ваших вопросов на https://stackoverflow.com/ — если вы никогда не пытались искать ответы там, вас ждет увлекательное путешествие!

Увидимся скоро, когда мы начнем с совершенно другого зверя языка, моего любимого (по крайней мере, когда речь идет об анализе) R!