Ми можемо зробити так, щоб деякі з нудних, повторюваних завдань, виконували не ми самі, а комп'ютери. Наприклад, можемо запрограмувати бота сканувати веб-сторінки, переходячи за посиланнями і завантажуючи файли, які він знайде. Знання, як програмувати і використовувати такого бота, можуть бути неймовірно корисною навичкою.
Python добре підходить для організації роботи веб-бота, який також називається в даному контексті павуком. Є кілька модулів, які треба завантажити, а потім зможете запрограмувати повнофункціонального бота, щоб виконати своє завдання, починаючи з будь-якої сторінки, яку задаєте. Обхід веб-сторінок і завантаження інформації - завдання не для страшно інтенсивної роботи процесора, тому добре підходить для Raspberry Pi. У той час, як ваш звичайний настільний комп'ютер обробляє більш складні обчислювальні задачі, RPi може обробляти легкі завдання, необхідні для завантаження веб-сторінок, щоб розібрати їх текст і перейти за посиланнями для завантаження файлів.
Бот-етикет
Одним з факторів, який треба мати на увазі, коли будуєте працюючий веб-сканер, це бот-етикет. Мається на увазі етикет в тому сенсі, що, швидше за все, є певні тонкощі, яких ви повинні дотримуватися при програмуванні бота для обходу сайтів.
Одним з них є необхідність поважати файл robots.txt. Більшість сайтів мають цей файл в кореневій папці сайту. Це простий текстовий файл, який містить інструкції для відвідування ботів і павуків. Якщо власник сайту не хоче сканування певних сторінок та їх індексування, то він перераховує ці сторінки і каталоги в текстовому файлі і ввічливі боти прислухаються до його запитів.
Формат файлу простий і виглядає наступним чином:
User-agent: *
Disallow: /examples/
Disallow: /private.html
Файл robots.txt вказує, що ніякі боти (User-Agent: *) не можуть відвідати (сканувати) будь-які сторінки в папці /examples/, а також вони не можуть відвідати сторінку private.html. Файл robots.txt є стандартним механізмом, за допомогою якого веб-сайти можуть обмежити відвідування певних сторінок. Якщо хочете, щоб вашого бота вітали на всіх сайтах, то буде гарною ідеєю слідувати цим правилам.
Далі пояснимо, як це зробити. Якщо ж ви вирішили ігнорувати ці правила, то часто можете очікувати, що ваш бот (і всі відвідування з вашої IP-адреси) будуть заборонені на сайті, про який йде мова.
Інша частина етикету контролює швидкість інформаційних запитів вашого бота. Оскільки ботами є комп'ютери, то вони можуть відвідувати і завантажувати сторінки та файли в сотні і тисячі разів швидше, ніж можуть це робити люди. З цієї причини цілком можливо для бота, що він зробить настільки багато запитів на сайті за настільки короткий проміжок часу, що може вивести з ладу погано налаштований веб-сервер. Тому ввічливо тримати запити бота до сторінки на керованому рівні: більшість власників сайтів вважають нормальним близько 10 запитів до сторінки в секунду, що набагато більше, ніж можна зробити вручну, але не досить, щоб зруйнувати сервер. Знову ж таки, в Python, це можна зробити за допомогою простої функції sleep().
І, нарешті, часто може бути проблемою підробка вашого особистого агента користувача. Ідентифікатор агента користувача ідентифікує відвідувачів на сайті. Браузери Firefox мають певного свого агента користувача, Internet Explorer має іншого, а боти - ще іншого. Тому, що є багато сайтів, які не хочуть давати всім ботам відвідувати або сканувати свої сторінки, деякі бот-розробники надають своїм ботам шахрайського агента користувача, щоб змусити його виглядати як звичайний веб-браузер. Це не круто. Вас ніколи не зможуть виявити, але це питання порядності, бо якщо у вас є сторінки, які б хотіли залишити конфіденційними, і ви хочете, щоб інші поважати ці ваші бажання, то робіть те ж саме для власників інших сайтів. Це всього лише частина того, щоб бути хорошим бот-розробником і користувачем Мережі. Ви можете імітувати агента користувача браузера, якщо емулюєте браузер для таких інших цілей, як тестування сайту або щоб знайти і завантажити файли (PDF-файли, MP3 і т.д.), але не для сканування цих сайтів.
Зв'язки веба
Перш, ніж ми перейдемо до програмування нашого павука, ви повинні трохи розуміти, як працює Інтернет. Так, це в основному гігантська комп'ютерна мережа, але ця мережа слідує певним правилам і використовує певні протоколи, і ми повинні використовувати ці протоколи для того, щоб зробити щось в Інтернеті, в тому числі, з використанням павука.
Протоколи веб-комунікацій
Hyper Text Transfer Protocol (HTTP) - протокол передачі гіпертексту - це формат, в якому втілено найбільш поширений веб-трафік. Протокол – це просто угода між двома сторонами з’єднання (в даному випадку, комп'ютерами), як це спілкування організувати. Він включає в себе таку інформацію, як, яким чином дані адресуються, як визначити, чи мали місце помилки під час передачі (і як обробляти ці помилки), як інформація повинна подорожувати між джерелом і пунктом призначення, який формат має ця інформація. "HTTP" перед більшістю URL (Uniform Resource Locators) визначає протокол, який використовується для запиту до сторінки. Інші поширені протоколи, які використовуються, такі: TCP/IP (Transmission Control Protocol/Internet Protocol), UDP (User Datagram Protocol), SMTP (Simple Mail Transfer Protocol) і FTP (File Transfer Protocol). Який протокол використовується, залежить від таких факторів, як тип трафіку, швидкість роботи запитів, чи потрібно потоки даних подавати в певному порядку і які помилки цих потоків можуть бути ігноровані чи виправлені.
Розглянемо, що відбувається за лаштунками при запиті веб-сторінки з вашого браузера. Припустимо, ви ввели http://www.irrelevantcheetah.com в рядку адреси. Ваш комп'ютер, знаючи, що він використовує протокол HTTP, спочатку відправляє www.irrelevantcheetah.com на свій локальний DNS-сервер (Domain Name System), щоб визначити, якій адресі в Інтернеті він належить. Сервер DNS відповідає з IP-адресою, скажімо, 192.185.21.158. Це адреса сервера, який містить веб-сторінку для цього домену. DNS прив’язує IP-адреси до імен, які набагато простіші для вас, і легше пам'ятати "www.irrelevantcheetah.com", ніж запам'ятати "192.185.21.158".
Тепер, коли ваш комп'ютер знає IP-адресу сервера, він ініціює TCP-з'єднання з цим сервером, використовуючи тристороннє «рукостискання». Сервер відповідає і ваш комп'ютер запитує сторінку "index.html". Сервер знову відповідає, а потім закриває з'єднання TCP.
Ваш браузер читає кодування на сторінці і відображає його. Якщо є інші частини сторінки, які йому необхідні, наприклад, PHP-код або зображення, він запитує ті частини або зображення з сервера і відображає їх також.
Формати веб-сторінок
Більшість веб-сторінок форматовано в HTML-HypeText Markup Language. Це форма XML (Extensible Markup Language), яку досить легко читати і аналізувати, і вона може бути зрозуміла більшості комп'ютерів. Браузери запрограмовані інтерпретувати мову сторінки і відображати ці сторінки в певному сенсі. Наприклад, пара тегів <html> і </html> вказують на те, що сторінка в HTML. <i> і </i> вказують, що укладений текст написаний курсивом, а <a> і </a> вказують гіперпосилання, яке зазвичай відображається синім кольором і підкресленням. JavaScript оточений тегами <script type="text/javascript"></script>. Також різні інші активні теги оточують різні мови і сценарії.
Всі ці теги і формати роблять перегляд і читання сирих веб-сторінок легким для людей. Проте, вони також мають ефект робити їх легким для комп'ютерів, щоб розібрати ці сторінки. Зрештою, якби ваш браузер не міг декодувати сторінки, то Інтернету не існувало б в його нинішньому вигляді. Але вам не потрібен браузер, щоб тільки запросити і прочитати веб-сторінки, відображаючи їх, як тільки отримали. Ви можете написати сценарій для запиту веб-сторінок, читання їх і виконання попередніх скриптових завдань для інформації на сторінках - все без втручання людини. Таким чином, ви можете автоматизувати довгий, нудний процес пошуку конкретних посилань, сторінок і відформатованих документів, переклавши його на RPi. Це і є веб-бот.
Приклад запиту
Для простоти, давайте почнемо з того, що ми запросили сторінку http://www.carbon111.com/links.html.
Текст даної сторінки досить простий - це статична сторінка, в кінці кінців, без фантастичної веб-форми або динамічного контенту, і виглядає подібно до цього:
<HTML>
<HEAD>
<TITLE>Links.html</TITLE>
</HEAD>
<BODY BACKGROUND="mainback.jpg" BGCOLOR="#000000"
TEXT="#E2DBF5" LINK="#EE6000" VLINK="#BD7603" ALINK="#FFFAF0">
<br>
<H1 ALIGN="CENTER">My Favorite Sites and Resources</H1>
<br>
<H2>Comix, Art Gallerys and Points of Interest:</H2>
<DL>
<DT><A HREF="http://www.alessonislearned.com/index.html" TARGET="blank">
A Lesson Is Learned...</A>
<DD>Simply amazing! Great ideas, great execution. I love the depth of humanity these two dig into. Not for the faint-of-heart ;)
.
.
і так далі, до кінцевого закриття тегом </HTML>.
Якщо павук отримав цю сторінку за допомогою з’єднання TCP, то спочатку дізнався б, що сторінка форматується в HTML. Далі б він вивчив заголовок сторінки і міг почати пошук для: а) зміст, який згідно поставленого завдання треба знайти (наприклад, файли в форматі .mp3 або .pdf) і б) посилання на інші сторінки, які будуть міститися в тегах <A> </A>. Павук також може бути запрограмований слідувати за посиланнями на певну "глибину". Іншими словами, ви можете вказати, чи повинен бот переходити за посиланнями на пов'язаних сторінках або йому слід припинити такі переходи за посиланнями після другого шару. Це дуже важливе питання, тому що, якщо програма має занадто багато шарів, то ваш павук може, в кінцевому підсумку, сканувати (і завантажувати) весь Інтернет – це стане критичною проблемою, якщо ваша полоса пропускання і обсяг зберігання обмежені!
Концепція нашого веб-бота
Концепція нашого веб-бота виглядає наступним чином: ми почнемо з певної сторінки, на основі призначеного користувачем входу. Потім визначимо, які файли ми шукаємо, наприклад, будемо шукати у вільному доступі файли робіт в форматі .pdf або вільно доступні .mp3 файли з нашими улюбленими групами? Цей вибір також буде запрограмований в нашому боті.
Бот буде починати з початку сторінки і аналізуватиме весь текст на сторінці. Він шукатиме текст, що міститься в тегах <a href> </a> (гіперпосилання.) Якщо гіперпосилання закінчується в ".pdf" або ".mp3" або іншим обраним типом файлу, то зробимо виклик wget (інструмент завантаження з командного рядка), щоб завантажити файл в наш локальний каталог. Якщо не можемо знайти будь-які посилання на обраний нами тип файлу, то будемо стежити за посиланнями, які знаходимо, повторюючи процес для кожного з цих посилань рекурсивно, як заздалегідь визначимо. Коли ми зайдемо так далеко, як хотіли, то повинні мати повний файлів каталог, які будуть переглянуті в довільний час. Це те, для чого веб-бот, дозволяючи комп'ютеру виконувати необхідну роботу, в той час, як ви насолоджувались лимонадом і чекали, щоб помилуватися плодами своєї праці.
Парсинг веб-сторінок
Парсинг відноситься до процесу в комп'ютері, коли той "читає" веб-сторінки. За своєю суттю, веб-сторінка є не більше, ніж потік даних, який складається з бітів і байтів (байт становить вісім біт), які декодуються, номерів форм, букв і символів. Хороша програма синтаксичного аналізу не тільки може повторно сформувати цей потік даних в потрібні символи, вона може зчитувати переформований потік і "розуміти", що вона читає. Веб-бот повинен бути в змозі розібрати сторінки, завантажені ним, так як ці сторінки можуть/повинні містити посилання на інформацію, на вилучення якої він запрограмований. Python має кілька доступних різних модулів синтаксичного аналізу, і ви повинні експериментувати, але найбільш корисним модулем вважаємо Beautiful Soup.
Примітка: Beautiful Soup названий на честь пісні Mock Turtle’s Льюїса Керролла (1855):
Beautiful Soup (бібліотека Python) пройшов через кілька версій; в цьому занятті використовується 4-та версія, яка працює в обох Python 2.x і 3.x.
Синтаксис Beautiful Soup досить простий. Після того, як ви встановили його, набравши:
sudo apt-get install python-bs4
можете почати використовувати його в своїх сценаріях. Відкрийте Python, набравши python і спробуйте ввести наступне:
import BeautifulSoup
Якщо отримаєте повідомлення про помилку, яка говорить: "No module named BeautifulSoup", ймовірно, ви використали бета-версію Beautiful Soup 4 (BS4) -версія за замовчуванням в цьому занятті. В такому випадку, введіть:
from bs4 import BeautifulSoup
Потім продовжуйте вводити:
import re
doc = ['<html><head><title>Page title</title></head>',
'<body><p id="firstpara" align="center">This is paragraph <b>one</b>.',
'<p id="secondpara" align="blah">This is paragraph <b>two</b>.',
'</html>']
soup = BeautifulSoup(''.join(doc)) # це два апострофа, один за іншим, а не подвійні лапки
Це завантажує файл з ім'ям doc, тому потік веб-сторінки буде виглядати як довгий єдиний потік символів. Потім Soup завантажує рядки в файл, який може бути розібраний бібліотекою. Якщо ви введете print soup в даний момент, то виведене буде виглядати так само, як результати введення print doc. Проте, якщо наберете:
print soup.prettify()
то будете винагороджені сторінкою, переробленою в більш доступну форму. Це просто приклад того, що може зробити Beautiful Soup. Поговоримо про нього більше, коли перейдемо до програмування бота.
Трохи пояснень: модуль re, імпортований вами в попередньому прикладі, використовується для оцінки регулярних виразів в тексті. Регулярні вирази, якщо ви не знайомі з ними, є надзвичайно універсальним способом пошуку за допомогою тексту і вибору рядків та послідовностей символів способами, які не можуть бути негайно очевидні для читача-людини. Терміни регулярного виразу можуть виглядати як повна маячня: хороший приклад регулярного виразу являє собою послідовність (?<=-)\w+, яка шукає послідовність символів в рядку, які йдуть після дефісу. Для того, щоб спробувати його, відкрийте командний рядок Python, набравши python, а потім введіть
import rem = re.search('(?<=-)\w+', 'free-bird')
m.group(0)
і ви будете винагородженні "bird".
У той час, як регулярні вирази дуже корисні з точки зору знаходження послідовності символів в тексті і рядках, вони також не дуже інтуїтивні і виходять далеко за рамки даного заняття. Не будемо тут витрачати багато часу на них. Досить того, що ви знаєте про те, що вони існують, і можете витратити деякий час на вивчення них, якщо вони вас цікавлять.
Програмування з модулями Python
Коли мова заходить про використання різних модулів Python при програмуванні вашого веб-павука, то ви маєте багато варіантів. Багато павуків з відкритим вихідним кодом вже існують і ви могли б запозичити їх, але буде хорошим досвідом, щоб запрограмувати павука з нуля.
Наш павук повинен буде зробити кілька речей, щоб виконати те, для чого він нам потрібний. Потрібно буде ініціювати TCP-з'єднання і запитувати сторінки, аналізувати отримані сторінки, завантажувати важливі файли, які він знаходить, і переходити за посиланнями, які трапляються. На щастя, більшість з них досить прості завдання, тому програмування нашого павука повинне бути відносно простим.
Використання модуля Mechanize
Ймовірно, найбільш використовуваний модуль, коли справа доходить до автоматизованого перегляду веб-сторінок, mechanize є одночасно неймовірно простим і неймовірно складним. Він простий у використанні і може бути встановлений за допомогою декількох простих рядків коду, але він також оснащений функціями, які багато користувачів не в повній мірі використовують. Це відмінний інструмент для автоматизації таких завдань, як тестування веб-сайту: якщо потрібно увійти в сайт 50 разів з 50 різними комбінаціями ім'я користувача/пароль, а потім заповнити форму адрес пізніше, mechanize - ваш інструмент вибору. Ще однією приємною річчю є те, що він робить велику частину роботи, наприклад, ініціювання з'єднань TCP і ведення переговорів з веб-сервером за лаштунками, тому можете зосередитися на частині, яка завантажується.
Щоб використовувати mechanize в сценарії, ви повинні спочатку завантажити та встановити його. Якщо ви слідували за нами, то Python все ще відкритий, але вам буде потрібний звичайний інтерфейс командного рядка для процесу завантаження і установки. Є два варіанти: ви можете вийти з режиму інтерфейсу розробника Python, або відкрити інший сеанс терміналу. Якщо вважаєте за краще мати відкритою тільки одну термінальну сесію, то вийдіть з командного рядка Python в поточному вікні, набравши Ctrl+d, який поверне до нормального рядку терміналу. З іншого боку, якщо вирішили відкрити інший сеанс терміналу, то можете залишити сеанс Python працюючим, і все, що ви набрали досі, буде в пам'яті.
Який би варіант ви не вибрали, введіть з командного рядку
wget http://pypi.python.org/packages/source/m/mechanize/mechanize-0.2.5.tar.gz
коли закінчиться завантаження, розпакуйте файл з
tar -xzvf mechanize-0.2.5.tar.gz
і перейдіть в папку з результатом, ввівши cd. Потім запустіть:
sudo python setup.py install
Дотримуйтесь усіх попереджень на екрані і mechanize буде встановлений та готовий до використання.
Парсинг з Beautiful Soup
Вище ми вже згадували парсинг: Beautiful Soup, як і раніше, кращий спосіб виконувати його. Надіємось, що Beautiful Soup ви вже встановили, як пропонувалося вище. Як тільки ви завантажуєте сторінку, Beautiful Soup реагує для пошуку посилань і передаючи їх до функції, яку будемо використовувати для завантаження, а також відкладає ті посилання, за якими буде переходити пізніше.
В результаті цього, однак, виявляється, що робота з пошуку посилань і визначення того, що завантажувати, має основну проблему з рядками. Іншими словами, посилання (і текст, що міститься в них) є нічим іншим, як рядки. І ми прагнемо розгадати ці посилання та перейти за ними або завантажити їх, тому будемо виконувати багато роботи з рядками - роботи, починаючи від lstrip (видалення символу зліва), щоб розділити, та різні інші методи з бібліотеки рядків. Мабуть, найцікавіша частина веб-бота, врешті-решт, це не файли, які він викачує; скоріше, це маніпуляції, які ви повинні зробити, щоб потрапити туди.
Завантаження з бібліотекою urllib
Останньою частиною головоломки є бібліотека urllib, зокрема, її функція URLopener.retrieve(). Ця функція використовується для завантаження файлів, плавно і без метушні. Ми передаємо їй ім'я нашого файлу і нехай вона робить свою справу.
Щоб використовувати urllib, ви повинні спочатку імпортувати її. Перемкніться на термінал з командним рядком Python, якщо він все ще відкритий, або почніть нову сесію, набравши python. Потім введіть:
import urllib
щоб зробити його доступним для використання.
Бібліотека urllib використовує такий синтаксис:
image = urllib.URLopener()
image.retrieve ("http://www.website.com/imageFile.jpg", "imageFile.jpg")
де перший параметр, що посилається в функцію URLopener.retrieve() є URL файлу, а другий параметр є локальним ім'ям файлу, під яким файл буде збережений. Другий параметр імені файлу вказує конвенції Linux для файлів і каталогів. Якщо ви передаєте йому параметр “../../imageFile.jpg”, то imageFile.jpg буде збережений через дві папки в дереві каталогів. Точно так же, передаючи йому параметр “pics/imageFile.jpg”, збережемо його в папку pics всередині поточної директорії (з якої запущений сценарій). Проте, папка повинна вже існувати; retrieve() не створить каталог.
Вирішуємо, що завантажити
На жаль (чи на щастя, в залежності від вашої точки зору), багато того, що можна завантажити, захищено авторським правом, так що навіть, якщо ви знайшли щось безкоштовне, насправді це не круто просто завантажити його. Перегляньте, що ви знайшли.
Однак, це тема з абсолютно іншого напрямку. Зараз, давайте припустимо, що ви збираєтеся шукати інформацію, яка вільно доступна, така, як всі твори Марка Твена, які знаходяться у вільному доступі. Це означає, що, ймовірно, буде шукати .pdf, .txt, і, можливо, навіть .doc або .docx файли. Можливо, ви навіть хочете розширити параметри свого пошуку, щоб включити .mobi (Kindle) і .epub файли, а також .chm. Це все законні формати файлів, які можуть містити тексти книг, які шукаєте.
Вибираємо відправну точку
Наступна річ, яку необхідно зробити, це вибрати відправну точку. Ви можете бути схильні просто сказати: "Google!", Але з десятками мільйонів результатів пошуку з простим пошуком "Марк Твен", вам, ймовірно, буде краще трохи більше сфокусуватися. Зробіть невелику основу заздалегідь, і збережете пізніше свої години роботи (і свого бота). Якщо, наприклад, можете знайти онлайновий архів робіт Твена, то це було б чудовою відправною точкою. Якщо шукаєте безкоштовне завантаження музики, то можете отримати загальний список блогів, які показують нові музичні файли вгорі і найновіші записи, бо багато нових музикантів дають скачати пісні на цих блогах геть безкоштовно з метою просування їх і їхньої музики. Крім того, технічні документи, що стосуються специфікацій IEEE мережі, ймовірно, можна знайти на технічному сайті або навіть урядовому сайті, з набагато більшим успіхом, ніж широкий пошук в Google.
Зберігаємо наші файли
Вам також може знадобитися місце для зберігання файлів, в залежності від розміру SD карти вашого RPi. Ця карта використовується як RAM і як місце для зберігання файлів, тому, якщо використовуєте карту 32 ГБ, то будете мати багато місця для файлів в форматі .pdf. Проте, 4-Гб карта може заповнитися досить швидко, якщо завантажуєте файли безкоштовних документальних фільмів. Так що вам знадобиться зовнішній USB жорсткий диск - повномасштабний жорсткий диск або флеш-диск меншого розміру.
Знову ж таки, деякий експеримент може стати в нагоді, тому що деякі зовнішні диски не зможуть добре працювати з RPi. Тому спочатку спробуйте їх.
Пишемо Python Bot
Давайте почнемо писати деякий код на Python. Наступний код імпортує необхідні модулі і використовує input (введення) версії мови Python, щоб отримати початкову точку (яка буде вставлятися перед "http://" в кожній веб-адресі). Потім він може ініціювати "browser" (з повітряними лапками) за допомогою mechanize.Browser(). Цей код, в своєму остаточному заповненому вигляді, наведений в кінці цього заняття.
Для того, щоб почати процес написання бота, використайте текстовий редактор (Leafpad або nano) і створіть новий файл з іменем webbot.py. Наберіть наступне (Python 2):
from bs4 import BeautifulSoup
import mechanize
import time
import urllib
import string
start = "http://" + raw_input ("Where would you like to start searching?\n")
br = mechanize.Browser()
r = br.open(start)
html = r.read()
Надалі нам, можливо, треба буде фальсифікувати агента користувача, в залежності від сайтів, які ми відвідуємо, але цей код на даний момент буде працювати.
Читання рядка і вилучення всіх посилань
Після того, як у вас є об'єкт браузера, який в попередньому коді називається br, то можете зробити всі види завдань з ним. Ми відкрили необхідну початкову сторінку від користувача з br.open() і прочитали його як один довгий рядок html. Тепер ми можемо використовувати Beautiful Soup, щоб прочитати цей рядок і витягти з нього всі посилання, додавши наступні рядки:
soup = BeautifulSoup(html)
for link in soup.find_all('a'):
print (link.get('href'))
Тепер можете запустити сценарій, щоб спробувати його. Збережіть його і закрийте. Відкрийте сеанс терміналу і перейдіть в той же каталог, в якому створили webbot.py. Потім введіть:
python webbot.py
щоб запустити програму, і введіть isearch.kiev.ua, коли він запитує, з чого почати. Вона повинна дещо повернути, а потім зупиниться:
Ви успішно прочитати вміст http://isearch.kiev.ua, витягнули посилання , і вивели це посилання на екран. Це дивовижний старт!
Наступним логічним кроком є створення списку посилань і додавання до цього списку щоразу, коли Beautiful Soup знаходить інше посилання. Після цього можете пройти по списку, відкриваючи кожне посилання з іншим об'єктом браузера і повторити процес.
Перегляд і завантаження файлів
Однак, перед тим, як ми створюємо список посилань, є ще одна функція, яку ми повинні створити - та, яка насправді шукає і завантажує файли! Тому давайте шукати код на сторінці для типу файлу. Ймовірно, ми повинні повернутися назад і запитати, який тип файлу шукаємо, додавши наступний рядок коду на початку сценарію після рядка старту:
filetype = input("What file type are you looking for?\n")
Тепер, коли ми знаємо, що шукаємо, додаємо кожне посилання до списку і можемо перевірити, чи це посилання на файл, який ми хочемо. Якщо шукаємо файли в форматі .pdf, наприклад, то можемо розібрати за посиланням, щоб побачити, чи вона закінчується pdf. Якщо так, то викликаємо URLopener.retrieve() і завантажуємо файл. Знову відкрийте свою копію webbot.py і замініть блоку коду наступним:
for link in soup.find_all('a'):
linkText = str(link)
if filetype in linkText:
#download file code here
Ви помітите два елементи в цьому маленькому фрагменті коду. По-перше, був доданий str(link). Beautiful Soup знаходить для нас кожне посилання на сторінці, але повертає його як об'єкт посилання, який є безглуздим без Soup-коду.
Нам потрібно перетворити його в рядок, щоб працювати з ним і робити все наші хитрі маніпуляції. Це те, що робить виклик методу str(). Насправді, Beautiful Soup пропонує спосіб, щоб зробити це для нас, але для навчання розібрати рядок з функцією str() важливо. По суті справи, саме тому ми використали рядок import string на початку нашого коду, тому тепер можемо взаємодіяти з об'єктами рядка.
По-друге, як тільки посилання є рядком, ви можете побачити, як можемо використати Python для виклику. Подібний до методу String.contains() в С#, Python для виклику просто шукає рядок, щоб побачити, чи містить він запитуваний підрядок. Тому, в нашому випадку, якщо шукаємо файли в форматі .pdf, то можемо шукати текст посилання для цього підрядка, "pdf". Якщо у ньому він є, то це посилання нас цікавить.
Тестування бота
Для того, щоб протестувати нашого бота простіше, автор створив сторінку в http://www.irrelevantcheetah.com/browserimages.html, яку використовує для тестування. Вона містить зображення, файли, посилання, а також різні інші HTML-ласощі. Використовуючи цю сторінку, ми можемо почати з чогось простого, типу зображень. Так що давайте змінимо код нашого webbot.py і щоб він виглядав наступним чином:
import mechanize
import time
from bs4 import BeautifulSoup
import string
import urllib
start = "http://mikrotik.kpi.ua/index.php"
filetype = raw_input ("What file type are you looking for?\n")
br = mechanize.Browser()
r = br.open(start)
html = r.read()
soup = BeautifulSoup(html)
for link in soup.find_all('a'):
linkText = str(link)
fileName = str(link.get('href'))
if filetype in fileName:
image = urllib.URLopener()
linkGet = "http://www.irrelevantcheetah.com" + fileName
filesave = string.lstrip(fileName, '/')
image.retrieve (linkGet, filesave)
Останній розділ цього коду, починаючи з циклу, вимагає деякого пояснення. В циклі перебираються всі посилання, які Beautiful Soup знайшов для нас. Потім linkText перетворює ці посилання в рядки, тому можемо маніпулювати ними. Потім перетворюємо тіло посилання (фактичний файл або сторінку, на яку вказує посилання) на рядок, а також перевіряємо, щоб побачити, чи він містить тип файлу, який шукаємо. Якщо так, то додаємо його до базового URL сайту, що надає нам linkGet.
Останні два рядки необхідні через функцію retrieve(). Як ви пам'ятаєте, ця функція приймає два параметри: URL файлу, який завантажуєте, і локальне ім’я, під яким ми хотіли б зберегти цей файл. filesave приймає fileName, знайдений раніше, і видаляє слеш "/" в імені, щоб ми могли зберегти його. Якщо не зробимо цього, то fileName спробує зберегти з папкою, наприклад, - /images/flower1.jpg. Якби ми спробуємо зберегти зображення з таким ім'ям, то Linux спробує зберегти flower.jpg в папку /images, а потім видає повідомлення про помилку, бо папка /images не існує. Знищивши слеш "/", ім'ям файлу буде images/flower1.jpg, і поки є папка images в нашій поточної директорії (пам'ятайте, що каталог треба створити першим), то файл буде збережений без інцидентів. Нарешті, останній рядок коду робить фактичне завантаження з двома параметрами, які вже згадували, linkGet і filesave.
Якщо ви створюєте каталог images в поточному каталозі, а потім запустите цей сценарій, відповідаючи на "jpg" на питання про тип файлу, то каталог images повинен заповнитися різними зображеннями. Просто, чи не так? Якщо замість цього ви створюєте каталог files і відповісте на питання "pdf", то отримаєте різні PDF-файли в папці files.
Створення каталогів і формування списку
Є ще дві особливості, які нам потрібно додати, щоб закінчити цей бот. По-перше, ми не завжди будемо знати, які каталоги нам потрібно створити завчасно, так що нам потрібно знайти спосіб, щоб розібрати ім'я папки з тексту посилання і створити каталог на льоту. По-друге, нам потрібно створити список посилань, які посилаються на інші сторінки, так щоб ми могли після цього відвідати ці сторінки і повторити процес завантаження. Якщо ми зробимо це кілька разів, то отримаємо для себе справжнього веб-бота, який буде переходити за посиланнями і завантажувати файли, які хочемо.
Давайте зробимо другу задачу першою - згенеруємо список посилань, які згадували раніше, коли розглядали читання рядків і вилучення посилань. Можемо створити список на початку сценарію, після операторів імпорту, і додати до нього перед запуском. Для створення списку просто використаємо
linkList = []
Щоб додати до нього, додаємо блок elif в наш сценарій:
if filetype in fileName:
image = urllib.URLopener()
linkGet = "http://www.irrelevantcheetah.com" + fileName
filesave = string.lstrip(fileName, '/')
image.retrieve (linkGet, filesave)
elif "htm" in fileName:
#covers both ".htm" and ".html" files
linkList.append(link)
Це воно! Якщо fileName містить тип посилання, яке шукаємо, воно витягується. Якщо цього не станеться, але в ньому є "htm", то це додається в список linkList, який зможемо перебирати, один за іншим, відкриваючи кожну сторінку і повторюючи процес завантаження.
Той факт, що ми будемо повторювати процес завантаження багато разів, повинен змусити вас думати про один елемент кодування: функцію, яка також називається методом. Пам'ятайте, що функція використовується в коді, якщо є процес, який повторюється знову і знову. Це робиться для більш чистого, більш простого коду, і це також легше писати.
Програмісти, як ви знаєте, є дуже ефективними людьми. Якщо ми можемо кодувати щось один раз і використовувати потім коли-небудь, то це набагато краще, ніж набирати код знову і знову. Це також значно економить час.
Отже, давайте почнемо програмувати нашу функцію завантаження, додавши наступні рядки в сценарій webbot.py, після рядка linkList = [], який додали трохи раніше:
def downloadFiles (html, base, filetype, filelist):
soup = BeautifulSoup (html)
for link in soup.find_all('a'):
linkText = str (link.get('href'))
if filetype in linkText:
image = urllib.URLopener()
linkGet = base + linkText
filesave = string.lstrip (linkText, "/")
image.retrieve (linkGet, filesave)
elif "htm" in linkText:
#covers both "html" and "htm"
linkList.append (link)
Тепер, коли ми маємо нашу функцію downloadFiles, все, що нам залишилося зробити, це розібрати наш linkText, щоб отримати ім'я каталогу, який ми повинні створити.
Знову ж таки, це просто маніпуляція з рядком разом з використанням модуля os. Модуль os дозволяє управляти папками і файлами, незалежно від того, яка операційна система запущена. По-перше, ми можемо додати
import os
в наш сценарій, а потім можемо створити каталог (якщо необхідно) шляхом додавання
os.makedirs()
Ви, можливо, пам'ятаєте, що для того, щоб спростити збереження файлів, нам потрібно мати локальний каталог на нашій машині, який відповідає веб-каталогу, для збереження наших цільових файлів. Для того, щоб побачити, який локальний каталог нам треба, необхідно спочатку визначити ім'я каталогу. У більшості (якщо не у всіх) випадків, каталог буде першою частиною нашого linkText. Наприклад, ім'я каталогу в /images/picture1.html це images. Таким чином, на першому кроці треба знову перебирати linkText, шукаючи слеші так само, як ми це зробили, щоб отримати базу імені нашого веб-сайту, наприклад:
slashList = [i for i, ind in enumerate(linkText) if ind == '/']
directoryName = linkText[(slashList[0] + 1) : slashList[1]]
Наведений вище код створює список індексів, в якому слеші знаходяться в рядку linkText. directoryName відділяється в linkText, як частина між першими двома косими рисками (слешами). (/images/picture1.html дозволяє вирізати images з нашого попереднього прикладу).
Перший рядок цього фрагмента вимагає деякого пояснення, тому що це важливий рядок коду. linkText є рядком, і як такий може перераховуватись; тобто символи всередині нього можна переміщувати один за іншим. slashList є списком позицій (індексів) в linkText, в яких знаходиться слеш. Після того, як перший рядок заповнить slashList, directoryName просто захоплює текст, що міститься між першою і другою косими рисками.
Наступні два рядки просто перевіряють, чи існує каталог, за що відповідає directoryName: якщо його немає, то створюємо його:
if not os.path.exists(directoryName):
os.makedirs(directoryName)
Це завершує нашу функцію downloadProcess, а з нею і наш простий веб-бот. Дайте йому спробувати, направляючи його на http://www.irrelevantcheetah.com/browserimages.html. Попросіть типи файлів "ipg", "pdf" або "txt" і подивіться як створюються папки і завантажуються файли - все без вашої допомоги.
Тепер, коли ви реалізували ідею, можете зійти з розуму від цього! Створення каталогів, перехід на три (і більше) рівнів в глибину, і перегляд того, що ваш бот завантажив для вас, поки ви займалися іншим! Половину задоволення отримаєте, коли іноді побачите, що отримали в завантаженому те, чого найменше очікували!
Заключний код
Тепер можете побачити остаточний, довгий код, який вище вводили шматочок за шматочком, якщо слідували у міру того, як ми розбиралися на занятті.
Настійно рекомендуємо вам набрати код, бо для навчання код буде набагато більш ефективним, якщо ви будете його вводити, а не просто копіювати і вставляти.
import mechanize
import time
from bs4 import BeautifulSoup
import re
import urllib
import string
import os
def downloadProcess (html, base, filetype, linkList):
"This does the actual file downloading."
soup = BeautifulSoup(html)
for link in soup.find_all('a'):
linkText = str(link.get('href'))
if filetype in linkText:
slashList = [i for i, ind in enumerate(linkText) if ind == '/']
directoryName = linkText[(slashList[0]+1):slashList[1]]
if not os.path.exists(directoryName):
os.makedirs(directoryName)
image = urllib.URLopener()
linkGet = base + linkText
filesave = string.lstrip(linkText, "/")
image.retrieve (linkGet, filesave)
elif "htm" in linkText:
#covers both "html" and "htm"
linkList.append(link)
start = "http://" + raw_input ("Where would you like to start searching?\n")
filetype = raw_input ("What file type are you looking for?\n")
numSlash = start.count('/')
#number of slashes in start—need to remove everything after third slash
slashList = [i for i, ind in enumerate(start) if ind == '/'] #list of indices of slashes
if (len(slashList) >= 3):
#if there are 3 or more slashes, cut after 3
third = slashList[2]
base = start[:third] #base is everything up to third slash
else:
base = start
br = mechanize.Browser()
r = br.open(start)
html = r.read()
linkList = [] #empty list of links
print "Parsing " + start
downloadProcess(html, base, filetype, linkList)
for leftover in linkList:
time.sleep(0.1) #wait 0.1 seconds to avoid overloading server
linkText = str(leftover.get('href'))
print "Parsing " + base + linkText
br = mechanize.Browser()
r = br.open(base + linkText)
html = r.read()
linkList = []
downloadProcess(html, base, filetype, linkList)
Висновки
Ви отримали чудові навики в Python, написавши веб-бота, або павука, який готовий для вас пройти через Інтернет і завантажити файли, які вважаєте цікавими, можливо, навіть під час вашого сну. Ви використовували функції, будували і додавали об'єкти в списки і навіть зробили кілька простих маніпуляцій з рядками.
Завдання: Запрограмуйте бота для витягування поштових адрес зі сторінок сайтів.
(За матеріалами: Wolfram Donat. Learn Raspberry Pi Programming with Python. – Apress, 2014, 244 p)