4
\$\begingroup\$

Hello I would like to use python to change the author of an html file, in order to do so I have written a few lines.

First I have used the HTMLParser class from the standard library to make a class that indicates which line of the html page needs to be changed.

from html.parser import HTMLParser

class AuthorFinder(HTMLParser):
    def __init__(self):
        super().__init__()
        self._edition_line = None

    def handle_starttag(self, tag, attrs):
    '''
    If the tag handled is the meta tag containing the author's name, then
    the value of _edition_line will be set to the current line (starting from 0)
    '''
    if tag.lower() == 'meta':
        for a_tuple in attrs:
            name, value = (field.lower() for field in a_tuple)
            if name == 'name' and value == 'author':
                # getpos() returns (line, column) starting from (1, 1)
                # whereas I want the first index to be 0.
                self._edition_line = self.getpos()[0] - 1

    @property
    def edition_line(self):
        return self._edition_line

I then use that class to get the line of the meta tag that contains the author. The following function changes the whole line and return and the new html page as a string.

def change_author(html_code, author):
    html_parser = AuthorFinder()
    html_parser.feed(html_code)
    author_line = html_parser.edition_line
    html_lines = html_code.splitlines()
    new_tag = '<meta name="author" content="{}">'.format(author)
    html_lines[author_line] = new_tag
    new_html = '\n'.join(html_lines)
    return new_html

From there you can easily change the author of several html files with something as simple as this:

# put the path to your html files in here
html_files = ['example1.html', 'example2.html']
new_author = 'Mario Luigi'
for filename in html_files:
    with open(filename, 'rt') as f:
        html_code = f.read()
    new_code = change_author(html_code, new_author)
    new_filename = '{}-NewAuthor.html'.format(filename.split('.')[0])
    with open(new_filename, 'w') as f:
        f.write(new_code)

Here is my first, quick attempt at automating file editing. I would like, in the future, to automate more changes but for now let's focus on this one. The script works fine but I'm pretty sure it can be improved in several ways as:

  • the class AuthorFinder will keep on parsing even though the desired meta tag has been found
  • if no meta tag with the author's name has been found, no author will be added
  • the previous indentation is ignored
  • ...

I will try to find ways to improve this, and hope to get suggestions from here as well. Thank you for reading, and good day.

\$\endgroup\$
  • \$\begingroup\$ Do you have some example data that could be used to run the program? \$\endgroup\$ – Alexander Cécile Nov 19 at 14:48
  • \$\begingroup\$ @AlexanderCécile I have made a github repo for this tiny project, it contains two html files for testing purpose. \$\endgroup\$ – Louisono Nov 20 at 9:04
  • \$\begingroup\$ Awesome, I'll check it out :) \$\endgroup\$ – Alexander Cécile Nov 20 at 21:11
  • \$\begingroup\$ The tag with the author is guaranteed to always be under '/html/head', right? \$\endgroup\$ – Alexander Cécile Nov 20 at 21:54
  • \$\begingroup\$ Also, what is your reasoning for choosing html.parser over something like BeautifulSoup or lxml? \$\endgroup\$ – Alexander Cécile Nov 20 at 22:32
1
\$\begingroup\$

I'm going to try writing a few different versions of your program. I think it would make for a nice, basic comparison of the various alternatives.

Of course, this all hinges on the number of changes, and their complexity, you wish to make. In that regard, it would be nice to get some more information on those changes, even if you haven't written the code for it yet.


Pure lxml, .find() with a simple XPath expression.

from lxml import etree

parser = etree.HTMLParser()
tree = etree.parse('../resources/author_parser_test_1.html', parser=parser)

author_tag = tree.find("./head/meta[@name='author']")

if author_tag is None:
    print("Couldn't find author tag")
else:
    author_tag.set('content', 'New Author')
    tree.write('../out/author_parser_test_1_res.html', method='html', pretty_print=False)

BeautifulSoup using lxml as the parser

from bs4 import BeautifulSoup

with open('../resources/author_parser_test_1.html', 'r') as file_1:
    soup = BeautifulSoup(file_1, features='lxml')

author_tag = soup.find('meta', attrs={'name': 'author'})

if author_tag is None:
    print("Couldn't find author tag")
else:
    author_tag['content'] = 'New author'
    with open('../out/author_parser_test_1_res.html', 'w') as file_1:
        file_1.write(soup.prettify())

I will keep updating this post as I work.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.