Természetesen ez az oldal is használ cookie-kat.
Nem tetszik?

Nem, elmegyek
home

T  h  e
D a r k
S i t e

Magánjellegű internetes dokumentációs és publikációs felület és kísérleti weboldal a Morkpy tartalomkezelő rendszer fejlesztési folyamatainak tesztelésére és szemléltetésére

Címke: python

Headless Firefox futtatása Pythonból Seleniummal

dark Dokumentáció 2021-01-15 11:22:00

Ezt csináljuk akkor, mikor nem elég a wgettel letöltött html forrás, mert kell a javascriptes, iframe-es tartalom is.


Szerezzük meg például megadott weboldal Facebook megosztásainak, vagy számát rendkívül elegáns módon.

Előkészület

Létrehozunk egy Python venvet és telepítjük a selenium nevű csomagot. Letöltjük a mozilla geckodriver t és telepítjük a Firefox ot, ha még nem tettük volna. Grafikus felületre nem lesz szükség.

Aztán

#!/usr/bin/env venv/bin/python
import os
import sys

from selenium.webdriver import Firefox
from selenium.webdriver.common.by import By
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.firefox.webelement import FirefoxWebElement
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.ui import WebDriverWait

options: Options = Options()
options.headless = True
executable_path = os.path.join(os.path.dirname(__file__), 'geckodriver')
url: str = sys.argv[1]

driver: Firefox = Firefox(options=options, executable_path=executable_path)
driver.get(url)
fbframe: FirefoxWebElement = WebDriverWait(driver, 10).until(
    expected_conditions.presence_of_element_located(
        (By.XPATH, '//iframe[starts-with(@data-testid, "fb:like")]')))
driver.switch_to.frame(fbframe)
span: FirefoxWebElement = WebDriverWait(driver, 10).until(
    expected_conditions.presence_of_element_located((By.ID, 'u_0_3')))
print(span.text)

driver.close()
driver.quit()

Megjegyzés

A szkriptet futtató felhasználónak kell, hogy legyen írási joga a home könyvtárára - ugye a www-data usernek nem szokott lenni - ennélkül nem fog működni. A home könyvtárban jön létre a .cache és a Firefox profilt tartalmazó .mozilla alkönyvtár.


Egy médiafigyelési feladatokat végrehajtó Python szkript vázlata

dark Dokumentáció 2020-12-08 20:25:00

Nevezhetjük úgy is, hogy rss letöltő és feldolgozó és html letöltő és feldolgozó.


A feledat lényege, hogy ismétlődő jelleggel letöltjük egy weboldal rss-ét, utána végighaladunk az egyes elemeken, letöltjük a linkelt html-t, abból pedig adatokat nyerünk ki és tárolunk. Így kell nekiállni:

Függőségek

RSS értelmező

pip install feedparser

HTML feldolgozó

pip install BeautifulSoup

A szkript

import datetime
import re
import sys

import bs4
import feedparser
import requests

feed: feedparser.FeedParserDict = feedparser.parse(sys.argv[1])

for entry in feed["entries"]:
    link: str = entry.link
    guid: str = entry.guid
    title: str = entry.title
    lead: str = entry.description
    published: datetime.datetime = datetime.datetime(
        *(entry.published_parsed[:6]))

    response: requests.Response = requests.get(link)
    soup: bs4.BeautifulSoup = bs4.BeautifulSoup(
        response.content.decode("utf-8", errors="ignore"), "html.parser")
    article_tag: bs4.element.Tag = soup.find(
           "div", attrs={"class": "content"})
    html_cleanup(article_tag)
    article_text: str = prettify(article_tag)
    article_author: str = soup.find("div", attrs={
        "class": "info"
    }).find("a").text

A segédfüggvények, amik kitakarítják a HTML-t, csak azokat a tag-eket és attribútumokat meghagyva, amik minimálisan szükségesek ahhoz, hogy a tartalom olvasható legyen.

def html_cleanup(tag: bs4.element.Tag) -> None:
    """
    Kitakarítja a html kódot

    A tag értéket ugye referenciaként vette át, úgyhogy visszatérési érték
    nem kell.
    """
    children = list(tag.recursiveChildGenerator())

    for child in children:
        try:
            if (not child):
                continue
            # kommentek törlése
            if (isinstance(child, bs4.element.Comment)):
                child.replaceWith("")
                continue
            # a sima szöveg elemeket békén hagyjuk
            if (isinstance(child, bs4.element.NavigableString)):
                continue

            # attribútumok törlése, kivéve href és src
            for k in list(child.attrs.keys()):
                if (k not in ("href", "src")):
                    del child[k]

            # javascript és css törlése
            if (child.name in ("style", "script")):
                child.decompose()
            # bizonyos tag-ek maradhatnak
            elif (child.name in ("a", "em", "p", "h1", "h2", "h3", "h4", "h5",
                                 "h6", "strong", "table", "td", "tr")):
                pass
            # képek átalakítása a képre mutató linkekre
            elif child.name == "img":
                child.replaceWith(
                    bs4.BeautifulSoup(
                        "{0}".format(child['src']),
                        "html.parser"))
            # minden további html tag eltávolítása
            else:
                child.unwrap()
        except Exception as e:
            print("Error", e)

def prettify(tag: bs4.element.Tag) -> str:
    """
    Szépen formázott html string
    """
    soup = bs4.BeautifulSoup("{}".format(tag.decode_contents()),
                             "html.parser")
    return (soup.prettify())

A legújabb, 3.9-es Python és a 5.2.1-es verziószámú feedparser nem biztos, hogy jóban lesznek egymással

Traceback (most recent call last):
  File "/tmp/tmp.py", line 6, in
    import feedparser
  File "/home/dark/morkpy_venv/lib/python3.9/site-packages/feedparser.py", line 93, in
    _base64decode = getattr(base64, 'decodebytes', base64.decodestring)
AttributeError: module 'base64' has no attribute 'decodestring'

Az ideiglenes megoldás:

import base64

base64.decodestring = base64.decodebytes

MySQL adatbázisból CSV fájlba Pythonban

dark Dokumentáció 2020-12-02 08:57:00


Előfordul, hogy adatázisból kell kimutatást készíteni, amit Excelben nézegethetnek, vagy éppen adatokat kell átvarázsolni MySQL-ből egy CSV formátumot váró rendszerbe. Nem bonyolult, de legutóbb megint keresgélnem kellett, hogy milyen API-n keresztül és hogyan lehet Pythonban lekérdezéseket végrehajtani, illetve, hogyan kell a csv writer-t paraméterezni, ezért most vázlatosan idedokumentálom, hogy legközelebb ne kelljen sokat gondolkozni, hogy hol keressem.

venv/bin/pip install mysqlclient
#!/usr/bin/env venv/bin/python3
import csv
import re
import sys

import MySQLdb

def main(db_host: str, db_db: str, db_user: str, db_passwd: str, outfile: str):
    db = MySQLdb.connect(host=db_host,
                         db=db_db,
                         user=db_user,
                         passwd=db_passwd,
                         charset='utf8')
    cursor = db.cursor(MySQLdb.cursors.DictCursor)

    cursor.execute("""\
        SELECT * FROM `user`
        JOIN `group` ON user_group_id = group.group_id
        WHERE user_created
        ORDER BY user_created DESC""")

    with open(outfile, "w") as csvfile:
        csvwriter = csv.writer(csvfile, delimiter=",")
        csvwriter.writerow(
            ("EMAIL", "FIRSTNAME", "PHONE", "CREATION_DATE", "GENDER"))

        for line in cursor.fetchall():
            email: str = line['user_email']
            firstname: str = line['group_desc']
            phone: str = ""
            creation: str = str(
                line['user_created'])[0:10]if line['user_created']else ""
            gender: str = str(line['user_gender']) or "0"

            csvwriter.writerow((email, firstname, phone, creation, gender))

    cursor.close()

if __name__ == "__main__":
    sys.exit(main(*sys.argv[1:]))