Анализ таблицы Википедии с Python

Я новичок в Python и недавно начал изучать сканирование в Интернете. В приведенном ниже коде синтаксически анализируется страница со списком Википедии S & P 500 и записываются данные конкретной таблицы в базу данных.

Хотя этот скрипт жестко запрограммирован, и я бы определенно интересовался некоторыми соображениями о выполнении одной и той же задачи несколько более общим образом (возможно, с помощью beautifulsoup), это не моя основная проблема. Я действительно задавался вопросом, было ли менее сложным или более «питоническим» способом сделать это.

import urllib.request
import re
import pymysql

# Open Website and get only the table on the page with the relevant data. In this hardcoded case 
table = urllib.request.urlopen("https://en.wikipedia.org/wiki/List_of_S%26P_500_companies#S.26P_500_Component_Stocks").read().decode("utf-8")
table = table.split("<table")[1]
table = table.split("\n")

# Define regex used for parsing and initialise list containers
tick_ident_nasdaq = 'href=\"http:\/\/www\.nasdaq\.com\/symbol\/'
tick_ident_nyse = 'href=\"https:\/\/www.nyse.com\/quote\/'
name_grab = '\">(.+)<\/a></td>'
cigs_grab = '^<td>(.+)</td>'

ticker, exchange, names, cigs, cigs_sub = ( [] for i in range(5))
match = False           

# Parse HTML output and write relevant td data to lists. 
# The list is "hardcoded", meaning after each match of either NASDAQ or NYSE ident, 
# the matching <td> as well as the next, the fourth and fifth <td> after that one get parsed.  

for i in range(len(table)):
    if bool(re.search(pattern = tick_ident_nasdaq, string = table[i])):
        ticker.append(re.search(pattern = name_grab, string = table[i]).group(1))
        exchange.append("NASDAQ")
        match = True

    elif bool(re.search(pattern = tick_ident_nyse, string = table[i])):
        ticker.append(re.search(pattern = name_grab, string = table[i]).group(1))
        exchange.append("NYSE")
        match = True

    if match == True:
        names.append(re.search(pattern = name_grab, string = table[i + 1]).group(1))
        names[-1] = re.sub(pattern = "&amp;", repl = "&", string = names[-1])
        cigs.append(re.search(pattern = cigs_grab, string = table[i + 3]).group(1))
        cigs[-1] = re.sub(pattern = "&amp;", repl = "&", string = cigs[-1])
        cigs_sub.append(re.search(pattern = cigs_grab, string = table[i + 4]).group(1))
        cigs_sub[-1] = re.sub(pattern = "&amp;", repl = "&", string = cigs_sub[-1])
        match = False

# Format Data in tuple format for database export
company_data = zip(ticker, exchange, names, cigs, cigs_sub)

# Establish database connection, empty companies table and rewrite list data to table    
try:
    conn = pymysql.connect(host = "localhost", user = "root", passwd = "pw", db = "db", charset = "utf8", autocommit = True, cursorclass=pymysql.cursors.DictCursor)
    cur = conn.cursor()
    cur.execute("DELETE FROM companies")
    cur.executemany("INSERT INTO companies (tickersymbol, exchange, name, cigs, cigs_sub) VALUES (\"%s\", \"%s\", \"%s\", \"%s\", \"%s\")", (company_data))   
finally:
    cur.close()
    conn.close()
8 голосов | спросил DatenBergwerker 26 FebruaryEurope/MoscowbSun, 26 Feb 2017 08:58:53 +0300000000amSun, 26 Feb 2017 08:58:53 +030017 2017, 08:58:53

2 ответа


11

Правильный инструмент

Как вы уже сказали, вы не используете подходящий инструмент для этой задачи: вы не может анализировать HTML с регулярными выражениями .

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

Простейший контейнер

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

Кроме того, вы можете воспользоваться этой возможностью, чтобы более просто переписать части, где вы добавляете что-то в список, а затем ссылаться на него с помощью my_list[-1]

company_data = []

for i in range(len(table)):
    if bool(re.search(pattern = tick_ident_nasdaq, string = table[i])):
        exchange = "NASDAQ"
    elif bool(re.search(pattern = tick_ident_nyse, string = table[i])):
        exchange = "NYSE"
    else:
        exchange = None
    if exchange:
        ticker = re.search(pattern = name_grab, string = table[i]).group(1)
        name = re.search(pattern = name_grab, string = table[i + 1]).group(1)
        name = re.sub(pattern = "&amp;", repl = "&", string = name)
        cig = re.search(pattern = cigs_grab, string = table[i + 3]).group(1)
        cig = re.sub(pattern = "&amp;", repl = "&", string = cig)
        cig_sub = re.search(pattern = cigs_grab, string = table[i + 4]).group(1)
        cig_sub = re.sub(pattern = "&amp;", repl = "&", string = cig_sub)
        company_data.append((ticker, exchange, name, cig, cig_sub))

Скомпилируйте ваше регулярное выражение

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

# Define regex used for parsing
tick_ident_nasdaq = re.compile('href=\"http:\/\/www\.nasdaq\.com\/symbol\/')
tick_ident_nyse = re.compile('href=\"https:\/\/www.nyse.com\/quote\/')
name_grab = re.compile('\">(.+)<\/a></td>')
cigs_grab = re.compile('^<td>(.+)</td>')
amp_re = re.compile("&amp;")

company_data = []

for i in range(len(table)):
    if bool(tick_ident_nasdaq.search(string = table[i])):
        exchange = "NASDAQ"
    elif bool(tick_ident_nyse.search(string = table[i])):
        exchange = "NYSE"
    else:
        exchange = None
    if exchange:
        ticker = name_grab.search(string = table[i]).group(1)
        name = name_grab.search(string = table[i + 1]).group(1)
        name = amp_re.sub(repl = "&", string = name)
        cig = cigs_grab.search(string = table[i + 3]).group(1)
        cig = amp_re.sub(repl = "&", string = cig)
        cig_sub = cigs_grab.search(string = table[i + 4]).group(1)
        cig_sub = amp_re.sub(repl = "&", string = cig_sub)
        company_data.append((ticker, exchange, name, cig, cig_sub))

"& амп;" и "&"

То, что вы пытаетесь сделать, при замене «&» с "&":

  • заслуживает, но вставляет функцию самостоятельно

  • действительно соответствует общей проблеме, уже решенной: декодирование объектов HTML .

ответил Josay 26 FebruaryEurope/MoscowbSun, 26 Feb 2017 11:30:42 +0300000000amSun, 26 Feb 2017 11:30:42 +030017 2017, 11:30:42
1

Более общий подход здесь может заключаться в использовании pandas.read_html() , которая может анализировать эту таблицу в DataFrame, с которым было бы очень удобно иметь дело позже - фильтр, срез, запись в базу данных (см. to_sql() ).

Вот пример кода для получения требуемого кадра данных S & P 500:

import pandas as pd
import requests


url = "https://en.wikipedia.org/wiki/List_of_S%26P_500_companies"
response = requests.get(url)

df = pd.read_html(response.content)[0]
print(df)

И, FYI, read_html() использует BeautifulSoup для разбора HTML под капотом.

ответил alecxe 27 FebruaryEurope/MoscowbMon, 27 Feb 2017 02:36:33 +0300000000amMon, 27 Feb 2017 02:36:33 +030017 2017, 02:36:33

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132