I just wrote a short function to parse SO jobs XML feed and return dicts containing information about each job entry.
Now, each job entry page is visited to grab info not present in the xml feed (company logo URL, salary info, etc.) through bs4
with couple functions in the get_so_extras
module.
parse_feed.py:
from datetime import datetime as dt
from xml.etree import ElementTree as etree
from get_so_extras import get_company_logo, get_so_salary
import urllib3
import json
import sys
def get_so_listings(url):
"""
Get remote job listings from stackoverflow.com xml feed
"""
http = urllib3.PoolManager()
try:
so_file = http.request("GET", url).data
except Exception as e:
print(e.args)
sys.exit()
so_root = etree.fromstring(so_file)
items = so_root.findall("channel/item")
# Parse SO xml feed in dict
so_listings = []
for e in items:
job_dict = {
"so_id": e.findtext("guid"),
"title": e.findtext("title"),
"so_url": e.findtext("link"),
"author": e.findtext(
"""{http://www.w3.org/2005/Atom}author/{http://www.w3.org/2005/Atom}name"""
),
"description": e.findtext("description"),
"published_at": str(
dt.strptime(
e.findtext("pubDate").strip(), "%a, %d %b %Y %H:%M:%S Z"
)
),
"updated_at": str(
dt.strptime(
e.findtext("{http://www.w3.org/2005/Atom}updated"),
"%Y-%m-%dT%H:%M:%SZ",
)
),
"company_logo_url": get_company_logo(e.findtext("link")),
"categories": [c.text for c in e.findall("category")],
}
# @TODO: get_company_logo(job_dict["so_url"])
so_listings.append(job_dict)
print("Jobs imported: {}".format(len(so_listings)))
# Helper - export to json, ideally pushed to db
with open('output1.json', 'w', encoding='utf-8') as f:
json.dump(so_listings, f, indent=4)
return True
And here the helper function used to grab company logo URLs from get_so_extras
module.
get_so_extras.py:
from bs4 import BeautifulSoup
import requests
import time
def get_company_logo(job_url):
"""
Get company logo from stackoverflow.com job listing page
"""
try:
page = requests.get(job_url)
soup = BeautifulSoup(page.text, 'html.parser')
logo = soup.find("div", attrs={'class': 's-avatar s-avatar__lg mr8 bg-white fl-shrink0'}).\
img["src"]
except Exception as e:
print(e)
pass
time.sleep(5) # Be kind
return logo
It'd be great to understand whether I made any obvious mistake or bad choice along the way.
Obviously calling get_company_logo
within the XML parsing loop slows things down, what would be the best approach to get those fields?
Probably checking if we're looking at a new company/listing from db before visiting the page to scrape the logo would be more efficient.
edit: also, given job listings have pretty much similar attributes, could it make sense to create a class listing
with attribute and a method to push it to db (with de-duplication logic perhaps)?
Be ruthless, thanks in advance.
<item>
tags inside<channel>
. Each listing has just a handful of fields, you can eyeball them to decide which you want to parse. \$\endgroup\$so_listings
) where a dict represents a job entry. I've ditched the JSON output in favour of pushing the entry to a MySQL db through SQLAlchemy after checking for duplicates though. \$\endgroup\$