2016-05-30 4 views
1

Это не очень хороший код, но у меня есть код, который захватывает ряд строк из HTML-файла и дает мне ряд строк: author, title, date, length, text. У меня 2000 файлов html, и я хочу пройти через все из них и записать эти данные в один файл csv. Я знаю, что все это должно быть завернуто в цикл for в конце концов, но до этого мне трудно понять, как перейти от получения этих значений к написанию их в файл csv. Мое мышление было создать список или кортеж, а затем написать, что строки в CSV-файле:Напишите строку строк (плюс число) в строку csv

the_file = "/Users/john/Code/tedtalks/test/transcript?language=en.0" 
holding = soup(open(the_file).read(), "lxml") 
at = holding.find("title").text 
author = at[0:at.find(':')] 
title = at[at.find(":")+1 : at.find("|") ] 
date = re.sub('[^a-zA-Z0-9]',' ', holding.select_one("span.meta__val").text) 
length_data = holding.find_all('data', {'class' : 'talk-transcript__para__time'}) 
(m, s) = ([x.get_text().strip("\n\r") 
     for x in length_data if re.search(r"(?s)\d{2}:\d{2}", 
             x.get_text().strip("\n\r"))][-1]).split(':') 
length = int(m) * 60 + int(s) 
firstpass = re.sub(r'\([^)]*\)', '', holding.find('div', class_ = 'talk-transcript__body').text) 
text = re.sub('[^a-zA-Z\.\']',' ', firstpass) 
data = ([author].join() + [title] + [date] + [length] + [text]) 
with open("./output.csv", "w") as csv_file: 
     writer = csv.writer(csv_file, delimiter=',') 
     for line in data: 
      writer.writerow(line) 

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

Заглядывая вперед: лучше или эффективнее обрабатывать 2000 файлов таким образом, снимая их с того, что я хочу, и записывая одну строку CSV за раз или лучше построить кадр данных в pandas, а затем написать это в CSV? (Все 2000 файлов = 160 МБ, поэтому они удалены, конечные данные не могут превышать 100 МБ, поэтому здесь не может быть большого размера, но в будущем может возникнуть проблема с будущим размером.)

+0

Если вы просто хотите CSV файл затем создать dataframe первым, а затем сохранение в CSV-файл не будет быстрее, чем просто создание csv. Кажется, в вашем коде очень много регулярных выражений, если вы можете загрузить пару своих файлов, я могу взглянуть на то, что, возможно, сделать код немного проще, что может сделать запись намного проще. Кроме того, 'writerow' принимает итеративный код, поэтому вы хотите передать список данных' writerow (["" .join (автор), название, данные, длина, текст]) ' –

+0

Это ** очень ** щедро вы, @PadraicCunningham. Текущая работа находится здесь: https://github.com/johnlaudun/tedtalks. В нем есть 'test' каталог с 3 файлами, который, я надеюсь, дает частичное объяснение того, почему мой код regex/soup настолько уродлив ... –

+0

Не беспокойтесь, я вижу несколько вещей, которые мы можем убрать из краткого Посмотрите, но завтра я буду выглядеть нормально, когда получу немного свободного времени. –

ответ

1

Это позволит получить все файлы и поместить данные в CSV-файл, вам просто нужно пройти путь к папке, которая содержит HTML-файлы и имя выходного файла:

import re 
import csv 
import os 
from bs4 import BeautifulSoup 
from glob import iglob 


def parse(soup): 
    # both title and author are can be parsed in separate tags. 
    author = soup.select_one("h4.h12.talk-link__speaker").text 
    title = soup.select_one("h4.h9.m5").text 
    # just need to strip the text from the date string, no regex needed. 
    date = soup.select_one("span.meta__val").text.strip() 
    # we want the last time which is the talk-transcript__para__time previous to the footer. 
    mn, sec = map(int, soup.select_one("footer.footer").find_previous("data", { 
     "class": "talk-transcript__para__time"}).text.split(":")) 
    length = (mn * 60 + sec) 
    # to ignore time etc.. we can just pull from the actual text fragment and remove noise i.e (Applause). 
    text = re.sub(r'\([^)]*\)',"", " ".join(d.text for d in soup.select("span.talk-transcript__fragment"))) 
    return author.strip(), title.strip(), date, length, re.sub('[^a-zA-Z\.\']', ' ', text) 

def to_csv(patt, out): 
    # open file to write to. 
    with open(out, "w") as out: 
     # create csv.writer. 
     wr = csv.writer(out) 
     # write our headers. 
     wr.writerow(["author", "title", "date", "length", "text"]) 
     # get all our html files. 
     for html in iglob(patt): 
      with open(html, as f: 
       # parse the file are write the data to a row. 
       wr.writerow(parse(BeautifulSoup(f, "lxml"))) 

to_csv("./test/*.html","output.csv") 
+0

Это просто прекрасно. Прошлой ночью, когда я шел домой, я понял, что пришло время попробовать некоторые «большие брюки python для мальчика» и переместить часть этого в функцию, поэтому вы также дали мне учебник по этому поводу. Несколько вопросов: во-первых, я хотел бы поблагодарить вас в скрипте. Все хорошо? И как бы вы хотели, чтобы вас зачисляли? Во-вторых, я вижу, что вы кодируете 'author' и' title' explicity, который создает 'b'string'' в csv. Почему? Еще раз спасибо. –

+0

Не совсем уверен, что здесь происходит. Когда я укажу его в «большой» каталог, который имеет то же самое 3 файла плюс 2000 больше, он выдает ошибку «UnicodeDecodeError:« utf-8 »кодек не может декодировать байт 0x80 в позиции 3131: недопустимый стартовый байт. Я отслеживаю это сейчас ... –

+0

Увлекательный. Направьте его на './Test', и он работает. Направьте его на './Talk', и это не так. –