Разное

Парсер страниц python: подробный видеокурс и программный код

Содержание

подробный видеокурс и программный код

В видеокурсе из семи уроков описывается парсинг сайтов с различной структурой при помощи Python третьей версии, библиотек requests и BeautifulSoup.

В этом видеокурсе Олег Молчанов подробно, не торопясь, рассказывает про парсинг сайтов при помощи Python 3. Раскрываются особенности парсинга многостраничных ресурсов, использования прокси с различными User-Agent, сохранения изображений и распознавания простого текста, а также быстрый мультипроцессорный парсинг сайтов.

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

В уроке рассматривается мультипроцессорный парсинг на примере сайта CoinMarketCap.com. Сначала приводится пример однопоточного парсинга. Затем рассматривается, как модифицировать программу для реализации мультипроцессорного подхода при помощи библиотеки multiprocessing. Сравниваются временные интервалы, необходимые для парсинга в один и несколько потоков. Попутно рассказывается об экспорте данных в csv-файл.

Обратите внимание: для установки BeautifulSoup для Python 3 в видео указана неправильная команда. Правильный вариант: pip install beautifulsoup4 (либо для систем с двумя версиями Python: pip3 install beautifulsoup4).

Программный код при однопоточном парсинге:

import csv
from datetime import datetime
import requests
from bs4 import BeautifulSoup


def get_html(url):
    response = requests.get(url)
    return response.text


def get_all_links(html):
    soup = BeautifulSoup(html, 'lxml')
    tds = soup.find('table',).find_all('td', class_='currency-name')
    links = []
    for td in tds:
        a = td.find('a', class_='currency-name-container').get('href')
        link = 'https://coinmarketcap.com' + a
        links.append(link)
    return links


def text_before_word(text, word):
    line = text.split(word)[0].strip()
    return line


def get_page_data(html):
    soup = BeautifulSoup(html, 'lxml')
    try:
        name = text_before_word(soup.find('title').text, 'price')
    except:
        name = ''
    try:
        price = text_before_word(soup.find('div', 
class_='col-xs-6 col-sm-8 col-md-4 text-left').text, 'USD')
    except:
        price = ''
    data = {'name': name,
            'price': price}
    return data


def write_csv(i, data):
    with open('coinmarketcap.csv', 'a') as f:
        writer = csv.writer(f)
        writer.writerow((data['name'],
                         data['price']))
        print(i, data['name'], 'parsed')


def main():
    start = datetime.now()
    url = 'https://coinmarketcap.com/all/views/all'
    all_links = get_all_links(get_html(url))
    for i, link in enumerate(all_links):
        html = get_html(link)
        data = get_page_data(html)
        write_csv(i, data)
    end = datetime.now()
    total = end - start
    print(str(total))
    a = input()

if __name__ == '__main__':
    main()

Программный код при использовании мультипроцессорного парсинга с 40 процессами:

from multiprocessing import Pool
import csv
from datetime import datetime
import requests
from bs4 import BeautifulSoup


def get_html(url):
    response = requests.get(url)
    return response.text


def get_all_links(html):
    soup = BeautifulSoup(html, 'lxml')
    tds = soup.find('table',).find_all('td', class_='currency-name')
    links = []
    for td in tds:
        a = td.find('a', class_='currency-name-container').get('href')
        link = 'https://coinmarketcap.com' + a
        links.append(link)
    return links


def text_before_word(text, word):
    line = text.split(word)[0].strip()
    return line


def get_page_data(html):
    soup = BeautifulSoup(html, 'lxml')
    try:
        name = text_before_word(soup.find('title').text, 'price')
    except:
        name = ''
    try:
        price = text_before_word(soup.find('div',
class_='col-xs-6 col-sm-8 col-md-4 text-left').text, 'USD')
    except:
        price = ''
    data = {'name': name,
            'price': price}
    return data


def write_csv(data):
    with open('coinmarketcap.csv', 'a') as f:
        writer = csv.writer(f)
        writer.writerow((data['name'],
                         data['price']))
        print(data['name'], 'parsed')


def make_all(link):
    html = get_html(link)
    data = get_page_data(html)
    write_csv(data)


def main():
    start = datetime.now()
    url = 'https://coinmarketcap.com/all/views/all'
    all_links = get_all_links(get_html(url))
    
    with Pool(40) as p:
        p.map(make_all, all_links)
    
    end = datetime.now()
    total = end - start
    print(str(total))
    a = input()


if __name__ == '__main__':
     main()

Характерные времена могут разниться в зависимости от оборудования и текущего состояния сайта.

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

Обратите внимание: при многостраничном парсинге таких сайтов, как Avito.ru, ваш IP может быть временно забанен на совершение парсинга.

import requests
from bs4 import BeautifulSoup
import csv


def get_html(url):
    r = requests.get(url)
    return r.text


def get_total_pages(html):
    soup = BeautifulSoup(html, 'lxml')
    divs = soup.find('div', class_='pagination-pages clearfix')
    pages = divs.find_all('a', class_='pagination-page')[-1].get('href')
    total_pages = pages.split('=')[1].split('&')[0]
    return int(total_pages)


def write_csv(data):
    with open('avito.csv', 'a') as f:
        writer = csv.writer(f)
        writer.writerow((data['title'],
                         data['price'],
                         data['metro'],
                         data['url']))


def get_page_data(html):
    soup = BeautifulSoup(html, 'lxml')
    divs = soup.find('div', class_='catalog-list')
    ads = divs.find_all('div', class_='item_table')
    for ad in ads:
        try:
            div = ad.find('div', class_='description').find('h4')
            if 'htc' not in div.text.lower():
                continue
            else:
                title = div.text.strip()
        except:
            title = ''
        try:
            div = ad.find('div', class_='description').find('h4')
            url = "https://avito.ru" + div.find('a').get('href')
        except:
            url = ''
        try:
            price = ad.find('div', class_='about').text.strip()
        except:
            price = ''
        try:
            div = ad.find('div', class_='data')
            metro = div.find_all('p')[-1].text.strip()
        except:
            metro = ''
        data = {'title':title,
                'price':price,
                'metro':metro,
                'url':url}
        write_csv(data)


def main():
    url = "https://avito.ru/moskva/telefony?p=1&q=htc"
    base_url = "https://avito.ru/moskva/telefony?"
    page_part = "p="
    query_par = "&q=htc"

    # total_pages = get_total_pages(get_html(url))

    for i in range(1, 3):
        url_gen = base_url + page_part + str(i) + query_par
        html = get_html(url_gen)
        get_page_data(html)


if __name__ == '__main__':
    main()

Во второй части видео о парсинге Avito внимание фокусируется на совмещении парсинга с извлечением информации из изображений. Для этой задачи вместо библиотеки BeautifulSoup используется библиотека Selenium, работающая непосредственно с браузером на вашем компьютере. Для совместной работы может потребоваться файл драйвера браузера. Захват изображений и преобразование в текстовые строки осуществляется при помощи библиотек pillow и pytesseract.

from selenium import webdriver
from time import sleep
from PIL import Image
from pytesseract import image_to_string

class Bot:

    def __init__(self):
        self.driver = webdriver.Chrome() # or Firefox() or smth else
        self.navigate()

    def take_screenshot(self):
        self.driver.save_screenshot('avito_screenshot.png')

    def tel_recon(self):
        image = Image.open('tel.gif')
        print(image_to_string(image))

    def crop(self, location, size):
        image = Image.open('avito_screenshot.png')
        x = location['x']
        y = location['y']
        width = size['width']
        height = size['height']
        image.crop((x, y, x+width, y+height)).save('tel.gif')
        self.tel_recon()

    def navigate(self):
        self.driver.get('https://www.avito.ru/velikie_luki/telefony/lenovo_a319_1161088336')
        button = self.driver.find_element_by_xpath(
            '//button[@class="button item-phone-button js-item-phone-button 
button-origin button-origin-blue button-origin_full-width 
button-origin_large-extra item-phone-button_hide-phone 
item-phone-button_card js-item-phone-button_card"]')
        button.click()

        sleep(3)

        self.take_screenshot()

        image = self.driver.find_element_by_xpath(
            '//div[@class="item-phone-big-number js-item-phone-big-number"]//*')
        location = image.location
        size = image.size

        self.crop(location, size)


def main():
    b = Bot()


if __name__ == '__main__':
    main()

В этом видео на более абстрактном примере локальной страницы даны дополнительные примеры работы методов find и find_all библиотеки BeautifulSoup. Показывается, как находить включающие информацию блоки при помощи методов parentfind_parent и find_parents. Описаны особенности работы методов next_element и next_sibling, упрощающих поиск данных в пределах одного раздела. В конце видео рассматривается, как сочетать поиск в BeautifulSoup с регулярными выражениями.

В пятом видео анализируется основной прием, помогающий избежать при парсинге бана или появления капчи посредством использования прокси-сервера с меняющимся адресом и изменения параметра User-Agent. Демонстрируется пример использования метода find_next_sibling. Рассмотрена передача IP и User-Agent при запросе библиотеки requests. Для использования актуального списка доступных прокси-серверов мы рекомендуем дополнить код парсером соответствующего сайта (в видео используются заранее подготовленные файлы со списками прокси и браузерных агентов).

import requests
from bs4 import BeautifulSoup
from random import choice, uniform
from time import sleep


def get_html(url, useragent=None, proxy=None):
    print('get_html')
    r = requests.get(url, headers=useragent, proxies=proxy)
    return r.text


def get_ip(html):
    print('get_ip')
    print('New Proxy & User-Agent:')
    soup = BeautifulSoup(html, 'lxml')
    ip = soup.find('span', class_='ip').text.strip()
    ua = soup.find('span', class_='ip').find_next_sibling('span').text.strip()
    print(ip)
    print(ua)
    print('---------------------------')


def main():
    url = 'https://sitespy.ru/my-ip'

    useragents = open('useragents.txt').read().split('\n')
    proxies = open('proxies.txt').read().split('\n')

    for i in range(10):
        # sleep(uniform(3, 6))

        proxy = {'http': 'http://' + choice(proxies)}
        useragent = {'User-Agent': choice(useragents)}
        try:
            html = get_html(url, useragent, proxy)
        except:
            continue
        get_ip(html)


if __name__ == '__main__':
    main()

В этом видеоуроке на примере двух сайтов объясняется, как обрабатывать сайты с ошибками или сайты, сделанные непрофессионалами. Первый ресурс https://us-proxy.org/ представляет пример сайта, который выглядит как многостраничный, но это лишь видимость – все данные загружаются на главную страницу при первом обращении к сайту. Во втором примере в структуре блога http://startapy.ru/ используется три вертикальных списка с необычным горизонтальным смещением размещаемых блоков при добавлении новых публикаций. В видеоуроке показано, как средства анализа браузеров в связи с некорректной версткой могут вводить нас в заблуждение, при том что BeautifulSoup отображает и использует исправленный вариант.

На примере сайта со свободными изображениями для фона рабочего стола https://www.hdwallpapers.in/ показывается процедура загрузки изображений с сайта – каждого в отдельную папку. Используются только библиотеки os (для работы с заданием пути сохранения файла) и requests. Показывается работа с блочной загрузкой файлов посредством метода iter_content.

import os
import requests

# url = 'https://www.hdwallpapers.in/thumbs/2018/spiral_silver_metal-t1.jpg'

# r = requests.get(url, stream=True)    # stream for partial download

# with open('1.jpg', 'bw') as f:
#     for chunk in r.iter_content(8192):
#         f.write(chunk)

urls = [
    'https://www.hdwallpapers.in/walls/messier_106_spiral_galaxy_5k-wide.jpg',
    'https://www.hdwallpapers.in/walls/helix_nebula_5k-wide.jpg',
    'https://www.hdwallpapers.in/walls/orion_nebula_hubble_space_telescope_5k-wide.jpg',
    'https://www.hdwallpapers.in/walls/solar_space-wide.jpg'
]


def get_file(url):
    r = requests.get(url, stream=True)
    return r


def get_name(url):
    name = url.split('/')[-1]
    folder = name.split('.')[0]
    if not os.path.exists(folder):
        os.makedirs(folder)
    path = os.path.abspath(folder)
    return path + '/' + name


def save_image(name, file_object):
    with open(name, 'bw') as f:
        for chunk in file_object.iter_content(8192):
            f.write(chunk)

def main():
    for url in urls:
        save_image(get_name(url), get_file(url))

if __name__ == '__main__':
    main()

Почему стоит научиться > сайты, или как написать свой первый парсер на Python

В этой статье я постараюсь понятно рассказать о парсинге данных и его нюансах.

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

Перейдем к этапам парсинга.

  • Поиск данных
  • Извлечение информации
  • Сохранение данных

И так, рассмотрим первый этап парсинга — Поиск данных.

Так как нужно парсить что-то полезное и интересное давайте попробуем спарсить информацию с сайта work.ua.
Для начала работы, установим 3 библиотеки Python.

pip install beautifulsoup4

Без цифры 4 вы ставите старый BS3, который работает только под Python(2.х).

pip install requests

pip install pandas

Теперь с помощью этих трех библиотек Python, можно проанализировать нашу веб-страницу.

Второй этап парсинга — Извлечение информации.

Попробуем получить структуру html-кода нашего сайта.
Давайте подключим наши новые библиотеки.

import requests
from bs4 import BeautifulSoup as bs
import pandas as pd

И сделаем наш первый get-запрос.

URL_TEMPLATE = "https://www.work.ua/ru/jobs-odesa/?page=2"
r = requests.get(URL_TEMPLATE)
print(r.status_code)

Статус 200 состояния HTTP — означает, что мы получили положительный ответ от сервера. Прекрасно, теперь получим код странички.

print(r.text)

Получилось очень много, правда? Давайте попробуем получить названия вакансий на этой страничке. Для этого посмотрим в каком элементе html-кода хранится эта информация.

<h3><a href="/ru/jobs/3682040/" title="Комірник, вакансия от 5 ноября 2019">Комірник</a></h3>

У нас есть тег h3 с классом «add-bottom-sm», внутри которого содержится тег a. Отлично, теперь получим title элемента a.

soup = bs(r.text, "html.parser")
vacancies_names = soup.find_all('h3', class_='add-bottom-sm')
for name in vacancies_names:
    print(name.a['title'])

Хорошо, мы получили названия вакансий. Давайте спарсим теперь каждую ссылку на вакансию и ее описание. Описание находится в теге p с классом overflow. Ссылка находится все в том же элементе a.

<p>Some information about vacancy.</p>

Получаем такой код.

vacancies_info = soup.find_all('p', class_='overflow')
for name in vacancies_names:
    print('https://www.work.ua'+name.a['href'])
for info in vacancies_info:
    print(info.text)

И последний этап парсинга — Сохранение данных.

Давайте соберем всю полученную информацию по страничке и запишем в удобный формат — csv.

import requests
from bs4 import BeautifulSoup as bs
import pandas as pd

URL_TEMPLATE = "https://www.work.ua/ru/jobs-odesa/?page=2"
FILE_NAME = "test.csv"


def parse(url = URL_TEMPLATE):
    result_list = {'href': [], 'title': [], 'about': []}
    r = requests.get(url)
    soup = bs(r.text, "html.parser")
    vacancies_names = soup.find_all('h3', class_='add-bottom-sm')
    vacancies_info = soup.find_all('p', class_='overflow')
    for name in vacancies_names:
        result_list['href'].append('https://www.work.ua'+name.a['href'])
        result_list['title'].append(name.a['title'])
    for info in vacancies_info:
        result_list['about'].append(info.text)
    return result_list


df = pd.DataFrame(data=parse())
df.to_csv(FILE_NAME)

После запуска появится файл test.csv — с результатами поиска.

«Кто владеет информацией, тот владеет миром» (Н. Ротшильд).

Всё о парсинге сайтов на Python

Три доклада с конференции Data Fest 4, объединяющей исследователей, инженеров и разработчиков, связанных с Data Science.

Анализ данных предполагает, в первую очередь, наличие этих данных. Первая часть доклада рассказывает о том, что делать, если у вас не имеется готового/стандартного датасета, либо он не соответствует тому, каким должен быть. Наиболее очевидный вариант — скачать данные из интернета. Это можно сделать множеством способов, начиная с сохранения html-страницы и заканчивая Event loop (моделью событийного цикла). Последний основан на параллелизме в JavaScript, что позволяет значительно повысить производительность. В парсинге event loop реализуется с помощью технологии AJAX, утилит вроде Scrapy или любого асинхронного фреймворка.

Извлечение данных из html связано с обходом дерева, который может осуществляться с применением различных техник и технологий. В докладе рассматриваются три «языка» обхода дерева: CSS-селекторы, XPath и DSL. Первые два состоят в довольно тесном родстве и выигрывают за счет своей универсальности и широкой сфере применения. DSL (предметно-ориентированный язык, domain-specific language) для парсинга существует довольно много, и хороши они, в первую очередь, тем, что удобство работы с ним осуществляется благодаря поддержке IDE и валидации со стороны языка программирования.

Для тренировки написания пауков компанией ScrapingHub создан учебный сайт toscrape.com, на примере которого рассматривается парсинг книжного сайта. С помощью chrome-расширения SelectorGadget, которое позволяет генерировать CSS-селекторы, выделяя элементы на странице, можно облегчить написание скрапера.

Пример с использованием scrapy:

import scrapy

class BookSpider(scrapy.Spider):
    name = 'books'
    start_urls = ['http://books.toscrape.com/']

    def parse(self, response):
        for href in response.css('.product_pod a::attr(href)').extract():
            url = response.urljoin(href)
            print(url)

Пример без scrapy:

import json
from urllib.parse import urljoin
import requests
from parsel import Selector

index = requests.get('http://books.toscrape.com/')
books = []

for href in Selector(index.text).css('.product_pod a::attr(href)').extract():
    url = urljoin(index.url, href)
    book_page = requests.get(url)
    sel = Selector(book_page.text)
    books.append({
        'title': sel.css('h2::text').extract_first(),
        'price': sel.css('.product_main .price_color::text')extract_first(),
        'image': sel.css('#product_gallery img::attr(src)').extract_first()
    })

with open('books.json', 'w') as fp:
    json.dump(books, fp)

Некоторые сайты сами помогают парсингу с помощью специальных тегов и атрибутов html. Легкость парсинга улучшает SEO сайта, так как при этом обеспечивается большая легкость поиска сайта в сети.

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

Какие существуют варианты хранения полученных данных? Можно извлекать информацию сразу, либо сохранять HTML-страницы для последующей обработки в исходном (около 100КБ) или сжатом (15 КБ) размере. Порядок обхода сайта для извлечения данных зависит от его объема и структуры. Большинство современных сайтов представляют собой весьма сложные конфигурации большого числа страниц со ссылками друг на друга, напоминающие паутину. Две стандартные стратегии обхода: в глубину и в ширину. Плюсы и минусы обхода в глубину:

  • небольшой размер очереди запросов
  • удобен для краулера одного сайта
  • может не подойти для обхода всех ссылок (т.к. глубина может быть очень большой или бесконечной)

Особенности в ширину:

  • подходит для обхода всех ссылок на сайте
  • большой размер очереди
  • реальная глубина — 2-4
  • возможные проблемы глубины в графе ссылок

Вторая часть доклада посвящена примерам ресурсов, предоставляющих готовые датасеты, собранные со всего интернета. В базе данных Common Crawl, например, хранится около 300 ТБ информации, которая обновляется каждый месяц. Сервис Web Data Commons извлекает из Common Crawl структурированные данные.

Как вести себя, если при сборе данных сервер блокирует ваши автоматические запросы, выдавая ошибки 403, 404, 503 и так далее? Во-первых, нужно запастись терпением и пожертвовать частотой отправки запросов. Ни один уважающий себя сервер не потерпит бесцеремонной продолжительной бомбардировки автоматическими запросами. Докладчик столкнулся с этим лично, проводя на досуге исследование популярности мемов на одном из популярных информационных ресурсов. Будучи заблокированным по IP, он написал письмо в техподдержку с просьбой разбанить его, на что получил автоматический ответ о разблокировке. Подумав, что, если ему отвечает автомат, то и просить о разблокировке можно автоматически, он написал бота, который при каждом бане отправлял поддержке письмо с текущим IP и слезной просьбой о разбане. Автоматы переписывались всю ночь, в течение которой докладчик выяснил, что время ожидания разбана растет экспоненциально.

Постепенно он учился быть похожим на человека не нарушая трех законов робототехники имитируя браузер: ставя временны′е заглушки перед запросами (при заполнении форм, кликах на кнопки, переходе по страницам, параллельных запросах и т.д.):
time.sleep(3)
генерируя случайные юзер-агенты:

и используя Tor для многоуровневой переадресации:

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

Пример

Пример кода, который использует генератор хэдеров и тор для определения текущего IP-адреса:


import os
import socket
import requests
import time

import socks
import stem.process
from stem import Signal
from stem.control import Controller
from user_agent import generate_user_agent

current_time = lambda: int(round(time.time()))

class Network:

    def switch_ip(self):
        self.controller.signal(Signal.NEWNYM)
        print("Switching IP. Waiting.")
        t = current_time()
        time.sleep(self.controller.get_newnym_wait())
        print("{0}s gone".format(current_time() - t))

    def print_bootstrap_lines(self, line):
        print(line)

    def init_tor(self, password='supersafe', log_handler=None):
        self.tor_process = None
        self.controller = None

        try:
            self.tor_process = stem.process.launch_tor_with_config(
                tor_cmd=self.tor_path,
                config=self.tor_config,
                init_msg_handler=log_handler
            )

            self.controller = Controller.from_port(port=self.SOCKS_PORT + 1)
            self.controller.authenticate(password)

        except:
            if self.tor_process is not None:
                self.tor_process.terminate()
            if self.controller is not None:
                self.controller.close()

            raise RuntimeError('Failed to initialize Tor')

        socket.socket = self.proxySocket

    def kill_tor(self):
        print('Killing Tor process')
        if self.tor_process is not None:
            self.tor_process.kill()

        if self.controller is not None:
            self.controller.close()

    def __init__(self):
        # путь к директории файлов браузера Tor. В данном случае, директория для OS X
        TOR_DIR = '/Applications/TorBrowser.app/Contents/Resources/TorBrowser/Tor/' 
        PASS_HASH = '16:DEBBA657C88BA8D060A5FDD014BD42DB7B5B736C0C248422F37C46B930'
        IP_ADDRESS = '127.0.0.1'
        self.SOCKS_PORT = 9150

        self.tor_config = {
            'SocksPort': str(self.SOCKS_PORT),
            'ControlPort': str(self.SOCKS_PORT + 1),
            'HashedControlPassword': PASS_HASH,
            'GeoIPFile': os.path.join(TOR_DIR, 'geoip'),
            'GeoIPv6File': os.path.join(TOR_DIR, 'geoip6')
        }

        self.tor_path = os.path.join(TOR_DIR, 'tor')
        # Setup proxy
        socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, IP_ADDRESS, self.SOCKS_PORT)

        self.nonProxySocket = socket.socket
        self.proxySocket = socks.socksocket


def main():

    try:
        network = Network()
        network.init_tor('supersafe', network.print_bootstrap_lines)

        url = 'http://checkip.amazonaws.com/'
        headers = {'User-Agent': generate_user_agent()}

        req = requests.get(url, headers=headers)
        print(req.content)
        network.switch_ip()
        req = requests.get(url, headers=headers)
        print(req.content)
    finally:
        network.kill_tor()

if __name__ == "__main__":
    main()

Парсинг сайтов на Python: подробный видеокурс и программный код

import csv

from datetime import datetime

import requests

from bs4 import BeautifulSoup

def get_html(url):

    response = requests.get(url)

    return response.text

def get_all_links(html):

    soup = BeautifulSoup(html, ‘lxml’)

    tds = soup.find(‘table’, id=‘currencies-all’).find_all(‘td’, class_=‘currency-name’)

    links = []

    for td in tds:

        a = td.find(‘a’, class_=‘currency-name-container’).get(‘href’)

        link = ‘https://coinmarketcap.com’ + a

        links.append(link)

    return links

def text_before_word(text, word):

    line = text.split(word)[0].strip()

    return line

def get_page_data(html):

    soup = BeautifulSoup(html, ‘lxml’)

    try:

        name = text_before_word(soup.find(‘title’).text, ‘price’)

    except:

        name = »

    try:

        price = text_before_word(soup.find(‘div’,

class_=‘col-xs-6 col-sm-8 col-md-4 text-left’).text, ‘USD’)

    except:

        price = »

    data = {‘name’: name,

            ‘price’: price}

    return data

def write_csv(i, data):

    with open(‘coinmarketcap.csv’, ‘a’) as f:

        writer = csv.writer(f)

        writer.writerow((data[‘name’],

                         data[‘price’]))

        print(i, data[‘name’], ‘parsed’)

def main():

    start = datetime.now()

    url = ‘https://coinmarketcap.com/all/views/all’

    all_links = get_all_links(get_html(url))

    for i, link in enumerate(all_links):

        html = get_html(link)

        data = get_page_data(html)

        write_csv(i, data)

    end = datetime.now()

    total = end — start

    print(str(total))

    a = input()

if __name__ == ‘__main__’:

    main()

Парсинг сайтов в Python

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


# файл site_parser.py

import requests               # библиотека для работы с http запросами

from bs4 import BeautifulSoup # разбор html

import time                   # работа со временем

import logging                # логирование

# шаблон URL у которого меняется идентификатор

URL = ‘https:/example.com/post/%d’

# заголовки запроса, представляемся браузером Firefox/45 на Mac OS

headers = {

    ‘User-Agent’: ‘Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:45.0) Gecko/20100101 Firefox/45.0’

}

# файл, куда будут сохраняться логи

logging.basicConfig(filename=»parser.log»)

def parse(start=1, limit=10):

    «»»

        Данная функция непосредственно предназначена для парсинга.

        Параметр start и limit — идентификаторы запроса — от и до.

    «»»

    # делим больую выгрузку на меньшие части

    # и записываем в файл

    f = open(‘parsed/parsed_{start}_{limit}.csv’.format(start=start, limit=limit), ‘a’, encoding=’utf-8′)

    # генерируем id из заданного диапазона

    for counter in range(start, limit):

        try:

            # отправляем запрос на сервер

            req = requests.get(URL % counter, headers=headers)

        

        # логируем ошибки с помощью встроенного модуля

        # и переходим на следующую итерацию

        except requests.ConnectionError as e:

            logging.error(‘Id={id}; message={message}’.format(id=counter, message=str(e)))

            continue

        except requests.Timeout as e:

            logging.error(‘Id={id}; message={message}’.format(id=counter, message=str(e)))

            continue

        except requests.RequestException as e:

            logging.error(‘Id={id}; message={message}’.format(id=counter, message=str(e)))

            continue

    

        # если статус не равен 404, т.е. страница найдена

        if req.status_code != 404:

            

            # создаем объект html парсера

            soup = BeautifulSoup(req.text, ‘lxml’)

            

            # и получаем нужный div блок с контентом

            info = soup.find(‘div’, {‘class’: ‘block_cont’})

            

            # форматируем строку по желанию

            text = ‘ID’ + str(counter) + ‘; ‘ + info.text.replace(‘  ‘, »).strip().replace(‘\n’, ‘;’)

            

            # выводим в консоль

            print(text)

            

            # записываем в файл

            f.write(text + ‘\n’)

            

            # останавливаем поток на 10мс

            time.sleep(0.1)

    f.close()

# создает диапазоны id

def create_ranges(start, stop, step):

    return [[i, i + step — 1] for i in range(start, stop, step)]


from site_parser import parse, create_ranges # импортируем наши функции

from threading import Thread                 # импортируем класс Thread для работы с потоками

# создаем поток 

def create_thread(start, limit):

    # параметр target — наша функция для парсинга, args — кортеж аргументов для нее

    thread = Thread(target=parse, args=(start, limit))

    thread.start() # запускаем поток

    # thread.join()

def execute():

    

    # создаем диапазоны идентификаторов

    ranges = create_ranges(1, 100000, 10000)

    for rng in ranges:

        # запускаем поток

        create_thread(*rng)

# запускаем программу

if __name__ == ‘__main__’:

    execute()

Программа начинает выгружать информацию в несколько потоков и отображать вывод в консоль.

Вот так просто на Python можно сделать парсинг сайта.

Парсинг сайтов Python 3 | Losst

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

Часто сложно получить необходимые вам данные с определенного сайта. Хотя некоторые веб-сайты пытаются предоставлять данные в простом структурированном формате, многие этого не делают. Парсинг, анализ, обработка и очистка данных — это распространенный ряд действий, которые применяются для сбора информации из интернета и составления собственной базы данных. Рано или поздно вам понадобится собрать некоторые данные из веб-сайтов и чтобы сделать все в точности так, как вам необходимо можно использовать программирование. Один из лучших инструментов — Python. В этой статье мы собрали несколько инструментов и фреймворков на Python, с помощью которых можно выполнять парсинг сайтов Python 3.

Прежде чем начать…

Прежде чем мы перейдем к инструментам подумайте зачем вы будете это делать и будьте честными. Парсинг сайтов на Python может означать много чего. Не сканируйте веб-сайты чтобы их дублировать и представить чужую работу как свою. Помните об авторских правах и лицензировании, а также о том, документы, которые вы парсите могут быть защищены авторским правом. Учитывайте файлы robots.txt. А также не отправляйте сайту запросы чаще, чем реальные пользователи иначе рискуете получить проблемы с доступностью.

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

Парсинг данных с сайтов на Python3

1. Pyspider

Давайте начнем с Pyspider. Это веб-краулер с веб-интерфейсом, который позволяет выполнять парсинг сайтов python 3 в несколько потоков и сразу же наблюдать за процессом парсинга. Этот расширяемый инструмент с поддержкой нескольких баз данных и очередей сканирования, а также множеством дополнительных функций, начиная от приоритета и заканчивая возможностью повторной попытки открытия страницы. PySpider поддерживает как Python 2, так и 3. Вы можете настроить более быстрое сканирование запустив несколько экземпляров приложения.

Функции и методы PySpider хорошо документированы, даже есть фрагменты кода, которые мы можете использовать в своих проектах. Кроме того, есть онлайн демонстрация работы, которая поможет вам получить представление о пользовательском интерфейсе. Лицензия на ПО — Apache 2, но программа доступна на GitHub.

2. MechanicalSoup

MechanicalSoup — это библиотека краулинга, созданная на основе очень популярной и невероятно универсальной библиотеки, выполняющей парсинг html python — Beautiful Soup. Если у вас нет никаких особых требований к сканированию, но вам нужно собрать несколько полей или ввести какой-либо текст, но вы не хотите создавать собственный парсер для этой задачи — то эта библиотека может стать отличным решением.

MechanicalSoup имеет лицензию MIT. Вы можете посмотреть более подробно как его использовать в примере исходного файла example.py на странице GitHub. На данный момент у проекта нет полной документации.

3. Scrapy

Scrapy — это фреймворк для Python, с помощью которого вы сможете создать собственный инструмент парсинга. В дополнение к средствам парсинга и разбора, он включает в себя возможности экспорта в такие форматы, как JSON или CSV, а также отправку данных бэкенду. Также есть ряд расширений для таких задач, как обработка файлов cookie, подмена User Agent, ограничения глубины сканирования, а также API для создания своих расширений.

Для изучения Scrapy можно использовать онлайн документацию или один из многих ресурсов сообщества. Кодовая база Scrapy доступна на GitHub под лицензией BSDv3. Если вы не хотите программировать, то можете использовать графический интерфейс Portia.

Другие

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

  • Cola — это высокоуровневый распределенный инструмент для обхода и парсинга сайтов, но работает только на Python 2. Последний раз обновлялся два года назад;
  • Demiurge — поддерживает Python 2 и Python 3. Еще один неплохой вариант, хотя разрабатывается он все еще медленно;
  • Feedparser — небольшая библиотека, предназначенная для разбора RSS или Atom лент.
  • Lassie — упрощает получение базового контента страницы, такого как описание, название, ключевые слова или список изображений;
  • RoboBrowser — еще одна простая библиотека для Python 2 или 3 с базовыми функциями, поддерживается нажатие кнопок и заполнение форм. Уже давно не обновлялась, но по-прежнему отличный выбор.

Выводы

Это далеко не полный список, и, конечно, если вы профессиональный программист, то вы можете написать собственный инструмент и не использовать ни один из фреймворков. Однозначно, существует еще множество отличных инструментов, и не только на языке Python. А с помощью какого инструмента вы выполняете парсинг данных с сайта python или на другом языке? Какой ваш любимый? Напишите в комментариях!

Источник: https://opensource.com

Какой выбрать Python фреймворк для системы парсинга сайтов? — Хабр Q&A

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

1. Ключевые особенности системы, которые усложняют выбор фреймворка, склоняя меня к написанию собственного велосипеда

  1. Наличие «ядра» системы, которое позволяет динамически подключать/отключать пауков без перезапуска всего сервиса с возможностью мониторинга работы этих пауков. Или наличие API, на который можно будет «накрутить морду».
  2. Разделение паука на «фетчер»: парсинг списков и получение «сырых» (raw html) документов для парсинга с сохранением последних в БД и «парсер»: преобразование «сырых» страниц в структурированные данные, с возможностью отдельного запуска «парсера». Одна из главных «фич». Требования к парсеру могут меняться и надо будет перепарсивать все документы сайта заново, коих может быть сотни тысяч. Разумеется без их повторного скачивания.
  3. Централизованное хранилище «сырых» и распарсенных данных.
  4. Распределённость — возможность запуска пауков на отдельных нодах. Так как это требование сильно усложняет предыдущее требование, можно его упростить — просто возможность использования прокси.
  5. Расписание — запуск по времени (каждый час, сутки…) как пауков, так и конкретных тасков в этих пауках, в том числе указание на одноразовый парсинг. Пример: на сайте есть sitemap.xml, содержащий ссылки на другие sitemap: sitemap-2016.xml, sitemap-2017.xml, sitemap-2018.xml; очевидно, что для 2016 и 2017 достаточен один проход фетчера, в то время как 2018 надо периодически просматривать, раз в день, например.
  6. Приоритеты — возможность указания приоритета для отдельного паука.
  7. Кэширование — поддержка заголовков Cache-Control и ручного указания: не кэшировать / кэшировать на время / кэшировать по заголовку Cache-Control

2. Некритичные хотелки, которыми можно поступиться

  1. Использование asyncio. Эта часть стандартной библиотеки Python уже вполне «устаканилась» и, на мой взгляд, потихоньку становится стандартом де факто для асинхронного программирования в Python.
  2. Простой деплой паука — добавили нового паука на сервер, зашли в «админку» на сайте, включили. Смотрим результаты, смотрим логи. Понизили приоритет. Отключили.

3. Инструменты, которые я просмотрел на текущий момент
В принципе выбор не велик и укладывается в три основных:

  1. PySpider
    Отличный инструмент, часто пользуюсь им, когда надо получить данные с какого-нибудь сайта. К сожалению некоторые особенности делают проблематичным его использование:
    • Непонятно как разделить на «фетчер» и «парсер», вернее как осуществить отдельный запуск только «парсера».
    • Нет встроенной поддержки прокси
    • Код пауков редактируется в веб-интерфейсе и не подразумевает использование из файла. Есть возможность запуска отдельного инстанса спайдера с файлом, но этот вариант не подходит — сотни инстансов PySpider убивает саму философию использования этого фрейворка. Да и непонятно как всё это удобно дебажить в IDE.

  2. Grab, а точнее Grab:spider
    Очень интересный легковесный фреймворк, но похоже отсутствует:
    • Возможность централизованного управления пауками
    • Расписание запуска отдельных тасков
    • Возможность осуществить отдельный запуск только «парсера».

  3. Scrapy
    Самый известный и распиаренный инструмент для написания пауков на Python. Вопросы к нему примерно схожи с вопросами по Grab:spider:
    • Расписание запуска отдельных тасков
    • Возможность осуществить отдельный запуск только «парсера».

Итого, что посоветуете: писать своё решение или попытаться использовать фреймворк?

19,1. HTMLParser — простой анализатор HTML и XHTML — документация Python 2.7.18

Примечание

Модуль HTMLParser был переименован в html.parser в Python.
3. Инструмент 2to3 автоматически адаптирует импорт при конвертации
ваши исходники на Python 3.

Исходный код: Lib / HTMLParser.py


Этот модуль определяет класс HTMLParser , который служит основой для
анализ текстовых файлов, отформатированных в HTML (язык гипертекстовой разметки) и XHTML.В отличие от парсера в htmllib , этот парсер не основан на парсере SGML.
в sgmllib .

класс HTMLParser. HTMLParser

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

Класс HTMLParser создается без аргументов.

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

Также определено исключение:

исключение HTMLParser. HTMLParseError

HTMLParser может обрабатывать поврежденную разметку, но в некоторых случаях
может вызвать это исключение при обнаружении ошибки во время синтаксического анализа.Это исключение предоставляет три атрибута: msg — краткое
сообщение с объяснением ошибки, белье — это номер строки на
который был обнаружен сломанной конструкцией, а смещение — количество
в строку, с которой начинается конструкция.

19.1.1. Пример приложения для синтаксического анализа HTML

В качестве базового примера ниже приведен простой анализатор HTML, который использует
HTMLParser класс для печати начальных тегов, конечных тегов и данных
по мере их появления:

 из HTMLParser импорт HTMLParser

# создать подкласс и переопределить методы обработчика
класс MyHTMLParser (HTMLParser):
    def handle_starttag (self, tag, attrs):
        print "Обнаружен начальный тег:", тег

    def handle_endtag (сам, тег):
        print "Обнаружен конечный тег:", тег

    def handle_data (self, data):
        print "Обнаружены некоторые данные:", data

# создаем экземпляр парсера и передаем ему HTML
parser = MyHTMLParser ()
парсер.feed ('   Тест  '
            ' 

Разбери меня!

')

Тогда на выходе будет:

 Обнаружен начальный тег: html.
Обнаружен начальный тег: голова
Обнаружен начальный тег: title
Обнаружены некоторые данные: Тест
Обнаружен конечный тег: название
Обнаружен конечный тег: голова
Обнаружен начальный тег: body
Обнаружен начальный тег: h2
Обнаружил некоторые данные: Разбери меня!
Обнаружен конечный тег: h2
Обнаружен конечный тег: body
Обнаружен конечный тег: html
 

Экземпляры HTMLParser имеют следующие методы:

HTMLParser. корма ( данные )

Подать текст синтаксическому анализатору. Он обрабатывается постольку, поскольку состоит из
комплектные элементы; неполные данные буферизуются до тех пор, пока не будет подано больше данных
Вызывается close () . data может быть либо unicode , либо
str , но рекомендуется передать unicode .

HTMLParser. закрыть ()

Принудительная обработка всех буферизованных данных, как если бы за ними следовал конец файла
отметка.Этот метод может быть переопределен производным классом для определения дополнительных
обработка в конце ввода, но переопределенная версия всегда должна вызывать
метод базового класса HTMLParser close () .

HTMLParser. сброс ()

Сбросить экземпляр. Теряет все необработанные данные. Это неявно вызывается в
время создания.

HTMLParser. getpos ()

Возвращает текущий номер строки и смещение.

HTMLParser. get_starttag_text ()

Вернуть текст последнего открытого начального тега. Обычно это не должно
необходимы для структурированной обработки, но могут быть полезны при работе с HTML «как
развернут »или для повторного создания входных данных с минимальными изменениями (пробел между
атрибуты

.

синтаксический анализ HTML — Использование HTMLParser в Python 3.2

Переполнение стека

  1. Около
  2. Продукты

  3. Для команд
  1. Переполнение стека
    Общественные вопросы и ответы

  2. Переполнение стека для команд
    Где разработчики и технологи делятся частными знаниями с коллегами

  3. Вакансии
    Программирование и связанные с ним технические возможности карьерного роста

  4. Талант
    Нанимайте технических специалистов и создавайте свой бренд работодателя

  5. Реклама
    Обратитесь к разработчикам и технологам со всего мира

  6. О компании

Загрузка…

  1. Авторизоваться
    зарегистрироваться

  2. текущее сообщество

.Парсер

— доступ к деревьям синтаксического анализа Python — документация Python 3.9.0


Модуль синтаксического анализатора предоставляет интерфейс для внутреннего анализатора Python и
компилятор байт-кода. Основная цель этого интерфейса — позволить Python
код для редактирования дерева синтаксического анализа выражения Python и создания исполняемого кода
из этого. Это лучше, чем пытаться разобрать и изменить произвольный Python.
фрагмент кода в виде строки, поскольку синтаксический анализ выполняется аналогично
код, образующий приложение.Это также быстрее.

Предупреждение

Модуль синтаксического анализатора устарел и будет удален в будущих версиях
Python. Для большинства случаев использования вы можете использовать абстрактный синтаксис
Этап генерации и компиляции дерева (AST) с использованием модуля ast .

Следует отметить несколько важных моментов, связанных с этим модулем.
использование созданных структур данных. Это не руководство по редактированию синтаксического анализа
деревья для кода Python, но некоторые примеры использования модуля парсера :
представлены.

Самое главное, хорошее понимание грамматики Python, обрабатываемой
требуется внутренний парсер. Для получения полной информации о синтаксисе языка см.
к Справочнику по языку Python. Парсер
сам создается из грамматической спецификации, определенной в файле
Грамматика / Грамматика в стандартном дистрибутиве Python. Деревья разбора
хранящиеся в объектах ST, созданных этим модулем, являются фактическим выходом из
внутренний парсер при создании функциями expr () или suite () ,
описано ниже.Объекты ST, созданные sequence2st () , точно
моделируйте эти структуры. Имейте в виду, что значения последовательностей, которые
считается «правильным», будет варьироваться от одной версии Python к другой, поскольку
переработана формальная грамматика языка. Однако перенос кода из одного
Версия Python для другой в качестве исходного текста всегда будет позволять правильные деревья синтаксического анализа
быть созданным в целевой версии, с единственным ограничением:
переход на более старую версию интерпретатора не будет поддерживать более свежие
языковые конструкции.Деревья синтаксического анализа обычно несовместимы с одним
версии к другой, хотя исходный код обычно был совместим с
основная серия релизов.

Каждый элемент последовательностей, возвращаемых функцией st2list () или st2tuple ()
имеет простую форму. Последовательности, представляющие нетерминальные элементы грамматики
всегда иметь длину больше единицы. Первый элемент — это целое число, которое
идентифицирует продукцию в грамматике. Этим целым числам даны символические имена.
в файле заголовка C Include / graminit.h и модуль Python
символ . Каждый дополнительный элемент последовательности представляет собой компонент
продукции, как распознано во входной строке: это всегда последовательности
которые имеют ту же форму, что и родитель. Важный аспект этой структуры
Следует отметить, что ключевые слова, используемые для идентификации типа родительского узла,
такие как ключевое слово , если в if_stmt , включены в
дерево узлов без какой-либо специальной обработки. Например, , если ключевое слово
представлен кортежем (1, 'if') , где 1 — числовое значение
связаны со всеми токенами NAME , включая имена переменных и функций
определяется пользователем.В альтернативной форме возвращается, когда информация о номере строки
запрашивается, тот же токен может быть представлен как (1, 'if', 12) , где
12 представляет собой номер строки, в которой был найден символ терминала.

Терминальные элементы представлены примерно так же, но без дочерних элементов.
элементы и добавление исходного текста, который был идентифицирован. Пример
из , если ключевое слово выше репрезентативно. Различные типы
терминальные символы определены в заголовочном файле C Include / token.h и
модуль Python , токен .

Объекты ST не требуются для поддержки функциональности этого модуля,
но предназначены для трех целей: позволить приложению амортизировать
стоимость обработки сложных деревьев синтаксического анализа, чтобы обеспечить представление дерева синтаксического анализа
который сохраняет пространство памяти по сравнению со списком или кортежем Python
представление, и облегчить создание дополнительных модулей в C, которые
управлять деревьями синтаксического анализа. В Python можно создать простой класс-оболочку, чтобы
скрыть использование объектов ST.

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

См. Также

Модуль символ

Полезные константы, представляющие внутренние узлы дерева синтаксического анализа.

Модуль токен

Полезные константы, представляющие конечные узлы дерева синтаксического анализа и функции для
значения узлов тестирования.

Создание объектов ST

Объекты

ST могут быть созданы из исходного кода или из дерева синтаксического анализа. При создании
объект ST из источника, различные функции используются для создания 'eval'
и 'exec' форм.

парсер. expr ( источник )

Функция expr () анализирует параметр source , как если бы это был вход
для компиляции (источник, 'файл.py ',' eval ') . Если синтаксический анализ успешен, объект ST
создается для хранения внутреннего представления дерева синтаксического анализа, в противном случае
возникает соответствующее исключение.

парсер. люкс ( источник )

Функция suite () анализирует параметр source , как если бы это был вход
до компиляция (исходный код, 'file.py', 'exec') . Если синтаксический анализ успешен, объект ST
создается для хранения внутреннего представления дерева синтаксического анализа, в противном случае
возникает соответствующее исключение.

парсер. последовательность 2-я ( последовательность )

Эта функция принимает дерево синтаксического анализа, представленное как последовательность, и строит
внутреннее представительство, если возможно. Если он может подтвердить, что дерево соответствует
к грамматике Python, и все узлы являются допустимыми типами узлов в версии узла
Python, объект ST создается из внутреннего представления и возвращается
к вызываемому. Если есть проблема с созданием внутреннего представления, или
если дерево не может быть проверено, возникает исключение ParserError .An
Не следует предполагать, что объект ST, созданный таким образом, правильно компилируется; обычный
исключения, вызванные компиляцией, все еще могут быть инициированы, когда ST ob

.Парсер

- доступ к деревьям синтаксического анализа Python - документация Python v3.0.1

Модуль синтаксического анализатора предоставляет интерфейс для внутреннего анализатора Python и
компилятор байт-кода. Основная цель этого интерфейса - позволить Python
код для редактирования дерева синтаксического анализа выражения Python и создания исполняемого кода
из этого. Это лучше, чем пытаться разобрать и изменить произвольный Python.
фрагмент кода в виде строки, поскольку синтаксический анализ выполняется аналогично
код, образующий приложение.Это также быстрее.

Примечание

Начиная с Python 2.5, гораздо удобнее переходить к реферату
Этап генерации и компиляции синтаксического дерева (AST) с использованием ast
модуль.

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

Самое главное, хорошее понимание грамматики Python, обрабатываемой
требуется внутренний парсер.Для получения полной информации о синтаксисе языка см.
to Справочник по языку Python . Парсер
сам создается из грамматической спецификации, определенной в файле
Грамматика / Грамматика в стандартном дистрибутиве Python. Деревья разбора
хранящиеся в объектах ST, созданных этим модулем, являются фактическим выходом из
внутренний парсер, созданный функциями expr () или suite (),
описано ниже. Объекты ST, созданные с помощью sequence2st () добросовестно
моделируйте эти структуры. Имейте в виду, что значения последовательностей, которые
считается «правильным», будет варьироваться от одной версии Python к другой, поскольку
переработана формальная грамматика языка.Однако перенос кода из одного
Версия Python для другой в качестве исходного текста всегда будет позволять правильные деревья синтаксического анализа
быть созданным в целевой версии, с единственным ограничением:
переход на более старую версию интерпретатора не будет поддерживать более свежие
языковые конструкции. Деревья синтаксического анализа обычно несовместимы с одним
версия к другой, тогда как исходный код всегда был совместим с продвижением вперед.

Каждый элемент последовательностей, возвращаемых st2list () или st2tuple ()
имеет простую форму.Последовательности, представляющие нетерминальные элементы грамматики
всегда иметь длину больше единицы. Первый элемент - это целое число, которое
идентифицирует продукцию в грамматике. Этим целым числам даны символические имена.
в заголовочном файле C Include / graminit.h и модуле Python
символ. Каждый дополнительный элемент последовательности представляет собой компонент
продукции, как распознано во входной строке: это всегда последовательности
которые имеют ту же форму, что и родитель. Важный аспект этой структуры
Следует отметить, что ключевые слова, используемые для идентификации типа родительского узла,
такие как ключевое слово if в if_stmt, включены в
дерево узлов без какой-либо специальной обработки.Например, ключевое слово if
представлен кортежем (1, 'if'), где 1 - числовое значение
связаны со всеми токенами NAME, включая имена переменных и функций
определяется пользователем. В альтернативной форме возвращается, когда информация о номере строки
запрашивается, тот же токен может быть представлен как (1, 'if', 12), где
12 представляет номер строки, в которой был найден символ терминала.

Терминальные элементы представлены примерно так же, но без дочерних элементов.
элементы и добавление исходного текста, который был идентифицирован.Пример
ключевого слова if выше является репрезентативным. Различные типы
терминальные символы определены в заголовочном файле C Include / token.h и
токен модуля Python.

Объекты ST не требуются для поддержки функциональности этого модуля,
но предназначены для трех целей: позволить приложению амортизировать
стоимость обработки сложных деревьев синтаксического анализа, чтобы обеспечить представление дерева синтаксического анализа
который сохраняет пространство памяти по сравнению со списком или кортежем Python
представление, и облегчить создание дополнительных модулей в C, которые
управлять деревьями синтаксического анализа.В Python можно создать простой класс-оболочку, чтобы
скрыть использование объектов ST.

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

См. Также

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

Создание объектов ST

объектов ST могут быть созданы из исходного кода или из дерева синтаксического анализа. При создании
объект ST из источника, различные функции используются для создания 'eval'
и формы exec.

parser.expr ( исходный код )
Функция expr () анализирует параметр source , как если бы это был вход
для компиляции (источник, 'файл.py ',' eval '). Если синтаксический анализ успешен, объект ST
создается для хранения внутреннего представления дерева синтаксического анализа, в противном случае
выбрасывается соответствующее исключение.
parser.suite ( исходный код )
Функция suite () анализирует параметр source , как если бы это был вход
для компиляции (источник, 'file.py', 'exec'). Если синтаксический анализ успешен, объект ST
создается для хранения внутреннего представления дерева синтаксического анализа, в противном случае
выбрасывается соответствующее исключение.
парсер.последовательность2st ( последовательность )

Эта функция принимает дерево синтаксического анализа, представленное как последовательность, и строит
внутреннее представительство, если возможно. Если он может подтвердить, что дерево соответствует
к грамматике Python, и все узлы являются допустимыми типами узлов в версии узла
Python, объект ST создается из внутреннего представления и возвращается
к вызываемому. Если есть проблема с созданием внутреннего представления, или
если дерево не может быть проверено, выдается исключение ParserError.An
Не следует предполагать, что объект ST, созданный таким образом, правильно компилируется; обычный
исключения, вызванные компиляцией, могут все еще инициироваться, когда объект ST
передается в compilest (). Это может указывать на проблемы, не связанные с синтаксисом
(например, исключение MemoryError), но также может быть вызвано такими конструкциями
в результате синтаксического анализа del f (0), который избегает синтаксического анализа Python, но является
проверяется компилятором байт-кода.

Последовательности, представляющие токены терминала, могут быть представлены как двухэлементные
списки формы (1, 'имя') или трехэлементные списки формы (1,
'имя', 56).Если присутствует третий элемент, предполагается, что это действительный
номер строки. Номер строки может быть указан для любого подмножества терминала.
символы во входном дереве.

parser.tuple2st ( последовательность )
Это та же функция, что и sequence2st (). Эта точка входа
поддерживается для обратной совместимости.

Преобразование объектов ST

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

.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

2021 © Все права защищены. Карта сайта