Вишкрібання у Web за допомогою Go



Переклад статті. Посилання на оригінал.

 https://medium.com/@shahidahmed.org/programming-in-go-for-web-scraping-aedf937e769d


Код виглядає просто. Розробка швидка. І при цьому Go є потужною мовою програмування загального призначення. Ви можете використовувати GO для розробки будь-якої програми. Незалежно від того, чи це програма на рівні ОС, чи служба масштабу Google, Go може стати вашим найкращим вибором як мовою розробки. У цій статті ми напишемо програму веб-збирання та покажемо, як можна швидко створити корисну та ефективну програму. Наша мета — написати програму, яка просто працює. Можливо, у нього не так багато наворотів. Добре. Спочатку ми напишемо базову програму, а потім створимо її, застосовуючи нові методи та найкращі практики. Багато новачків і ентузіастів Go знаходять цей підхід цікавим.


Наше завдання полягає в тому, щоб зібрати деяку інформацію про фільми Disney, випущені на даний момент. Якщо ми шукаємо в Google «повний список фільмів Disney», ми отримуємо багато посилань. Одна з них з IMDB — популярного веб-ресурсу фільмів. Давайте клацнемо URL: Link. З’явиться сторінка під назвою «Повний список фільмів Уолта Діснея». Зрозуміло, що ми в правильному місці.


Сторінка виглядає забрудненою всілякою рекламою, кнопками, посиланнями та зображеннями. Але ми також бачимо інформацію про фільми в напів табличному форматі. Це наша мета. Ми очистимо цю частину сторінки, витягнемо необхідні фрагменти інформації та збережемо їх у текстовому файлі на нашому комп’ютері. От коротко про те шо ми хочемо зробити.


Спочатку нам потрібно поглянути на внутрішню структуру сторінки. У Chrome або Microsoft Edge є простий спосіб зробити це. Зберігаючи сторінку в браузері, запустіть Інструменти розробника (у розділі «Додаткові інструменти» в головному меню), і ви побачите повний DOM сторінки. У Firefox запустіть Web Developer, а потім Inspector. Ви побачите сторінку.


Не так просто пройти це дерево DOM і знайти конкретні слова. Отже, давайте скористаємося пошуком, набравши <ctrl-F> і знайшовши назву першого фільму — «Білосніжка». Так, тепер ми бачимо «Білосніжку та семеро гномів», прихованих під кількома ієрархічними рівнями <div..>, <span..>, <p..> та інших елементів. Завдання полягає в тому, як програмно розмістити їх у середині такої складної структури. На щастя, Go має багатий набір бібліотек з відкритим вихідним кодом для роботи майже з усім, що тільки можна придумати — мережею, базою даних, мультимедіа, Інтернетом речей, машинним навчанням тощо. Також є деякі бібліотеки для веб-збирання. Найпопулярніший, мабуть, Коллі. Ми будемо використовувати його для наших потреб.

Перш ніж почати кодування, давайте визначимо основні кроки, які нам потрібно реалізувати:

  • Визначити вхідні дані (веб-сторінка для сканування) і вихід (текстовий файл для запису зібраної інформації)

  • Створити і відкрити текстовий файл

  • Записати заголовки стовпців

  • Ініціалізувати процедуру очищення

  • Витягнути необхідну інформацію про кожен фільм

  • Записати в текстовий файл

  • Закрити текстовий файл

Давайте зараз напишемо програму.


Перший розділ схожий на шаблонний код із інформацією про імпорт пакета. Бібліотеки Colly імпортуеться рядком «github.com/gocolly/colly», «os» для створення/маніпулювання текстовим файлом і «encoding/csv» для вихідного тексту у форматі CSV. Усі ці бібліотеки є частиною стандартного набору Golang, за винятком Colly, яка є бібліотекою третьої сторони. Давайте встановимо Colly у вашому середовищі розробки, виконавши це:


go get github.com/gocolly/colly


Далі ми визначаємо вхідні дані (URL-адресу сторінки, яку потрібно отримати), створюємо вихідний текстовий файл і записуємо в текстовий файл заголовки стовпців, як-от серійний номер, назва фільму, рік випуску тощо. Потім настає найцікавіша частина коду — сканування за допомогою Colly.

В основі Colly лежить об’єкт Collector. Він обробляє дві речі — 1) мережевий зв’язок і 2) виконання приєднаних зворотних викликів під час виконання завдання збирача. Щоб працювати з Colly, нам потрібно спочатку створити екземпляр об’єкта Collector. А поки давайте створимо об’єкт за замовчуванням без налаштування.


c := colly.NewCollector()


Тепер нам потрібно приєднати різні функції зворотного виклику до об’єкта Collector, щоб обробляти різноманітні події, пов’язані з мережевим запитом (request) і відповіддю (response). Ось кілька важливих зворотних викликів, які можна використовувати (вони викликаються в зазначеному порядку):


OnRequest() — викликається безпосередньо перед тим, як зробити запит


OnError() — викликається, якщо під час запиту виникає будь-яка помилка


OnResponse() — викликається одразу після отримання відповіді


OnHTML() — викликається, коли Colly виявляє певний текст у відповіді HTML


Найважливішим зворотним викликом для наших потреб є OnHTML(). Ось код:


c.OnHTML(`.lister-item-content`, func(e *colly.HTMLElement) { ….. }


Він інструктує Colly, що слід виконувати наступні операції, коли знаходить елемент <div> із class=”lister-item-content”. "." (крапка) перед «lister-item-content» вказує, що це для атрибута «class», наприклад, <div class="lister-item-content" …>. Замість класу деякі елементи <div> можуть використовувати такий «id»: <div id=”content-extra-header” …>. У цьому випадку ми б використали «#» замість «.».


Таким чином, якщо Colly знаходить елемент <div class=”lister-item-content” …> будь-де в HTML-документі, він починає звідти і виконує інструкції, вказані в блоці коду. Деякі з цих інструкцій і відповідних операцій:


number := e.ChildText(“.lister-item-index”)


Витягнути значення елемента <span class=”lister-item-index” …>


name := e.ChildText(“.lister-item-index ~ a”)


Витягнути значення елемента <a href=”…”> який є братом елемента <span class=”lister-item-index” …>.


rating := e.ChildText(“[class=’ipl-rating-star small’] .ipl-rating-star__rating”)


Витягнути  значення елемента <span class=”ipl-rating-star__rating”> який є нащадком елемента <div class=”ipl-rating-star small” …>.


vote := e.ChildAttr(“span[name=nv]”, “data-value”)


Витягнути значення “data-value” атрибута, де елемент виглядає <span name=”nv” data-value=”xxx”>.


gross := e.ChildText(“.text-muted:contains(‘Gross’) ~ span[name=nv]”)


Витягнути значення елемента <span name=”nv”> який є братом елемента <div class=”text-muted” …> значення якого містить слово “Gross”.


Ми також можемо витягнути валову суму за допомогою такого селектора:


gross = e.ChildText(“[class=’text-muted text-small’] span:contains(‘$’)”)


Витягнути  значення елемента <span ..> якщо його значення містить рядок «$» і воно є нащадком <p class=’text-muted text-small”>.


Пам’ятайте, що для обходу дерева DOM Colly використовує GoQuery, яка є реалізацією Go jQuery — широко використовуваної бібліотеки Java для роботи з DOM. Це означає, що якщо у вас є складний селектор, який працює в jQuery, він також повинен працювати в Colly.


Повернемося до нашого коду. У зворотному виклику OnHTML() ми спочатку витягуємо всю необхідну інформацію. Потім ми записуємо їх у текстовий файл. Отже, зворотній дзвінок готовий.

c.Visit(fetchURL)


Це завершальний рядок. Тут ми повідомляємо об’єкту Collector відвідати сторінку, вказану fetchURL. Тепер збирач зробить мережевий запит і отримає відповідний HTML-документ. Під час цього процесу щоразу, коли він бачить такі події, як OnHTML(), він викличе відповідні зворотні виклики, які збиратимуть пов’язані фрагменти інформації та записуватимуть їх у вихідний файл. Це воно. Зішкріб майже завершено.


Майже, але ще не повністю. У нас є два пункти, що очікують на розгляд. Один — скинути об’єкт запису, щоб гарантувати, що весь текст у буфері буде записаний у файл, а інший — закрити текстовий файл. Ми вже подбали про них за допомогою цих двох операторів defer:


defer file.Close()

defer writer.Flush()


Ми знаємо, що всі виклики defer надходять у стек. Коли функція виклику завершена, відкладені виклики defer виконуються в порядку «останній прийшов-перший вийшов» (LIFO). Таким чином, у нашій програмі спочатку буде змитий (flushed) автор. Потім вихідний файл буде закрито. І програма закінчилася.

Ось друга частина нашої повної програми. Щоб отримати повний вихідний код із Github, використовуйте цю URL-адресу:https://github.com/shahidcc/Go-for-web-scraping


Ми припускаємо, що ви вже налаштували середовище розробки Go на своєму комп’ютері. Отже, ви можете запустити цю програму, перейшовши до терміналу та ввівши команду:

go run scrape-1.go


Якщо ви хочете створити виконувану програму, введіть команду:


go install scrape-1.go


Він створить один виконуваний файл під назвою «scrape-1.exe» у папці bin (Windows). Ви можете будь-коли поділитися файлом з іншими для запуску на їхніх комп’ютерах.


Таким чином, ми написали швидку та брудну програму, яка очищає необхідну інформацію з веб-сторінки та скидає її в текстовий файл у форматі CSV. У наступній статті ми розглянемо два складних випадки використання. Одним із них було б перейти за посиланнями, наведеними на сторінці, і зайти вглиб інших підсторінок. Інший — запустити багато процедур скрапінгу одночасно, щоб ми могли збирати інформацію з кількох сайтів паралельно та збирати її в один вихідний файл. А поки насолоджуйтесь програмуванням у Go. Це дуже весело *