I have just written this code to scrape some data from a website. In its current state it works fine, however, going by my tests on the script, I discovered that with the amount of data I am processing, it will take a few days to finish the task, Is there a way to improve its performance? I will insert a sample of the data as the bulk of it.
Input data in CSV format:
Code Origin
1 Eisenstadt
2 Tirana
3 St Pölten Hbf
6 Wien Westbahnhof
7 Wien Hauptbahnhof
8 Klagenfurt Hbf
9 Villach Hbf
11 Graz Hbf
12 Liezen
Code:
# import needed libraries
import csv
from datetime import datetime
from mechanize import Browser
from bs4 import BeautifulSoup
def datareader(datafile):
""" This function reads the cities from csv file and processes
them into an O-D for input into the web scrapper """
# Read the csv
with open(datafile, 'r') as f:
reader = csv.reader(f)
header = reader.next()
ListOfCities = [lines for lines in reader]
temp = ListOfCities[:]
city_num = []
city_orig_dest = []
for i in ListOfCities:
for j in temp:
ans1 = i[0], j[0]
if ans1[0] != ans1[1]:
city_num.append(ans1)
ans = (unicode(i[1], 'iso-8859-1'), unicode(j[1], 'iso-8859-1'),i[0], j[0])
if ans[0] != ans[1] and ans[2] != ans[3]:
city_orig_dest.append(ans)
yield city_orig_dest
input_data = datareader('BAK.csv') # Input data here
def webscrapper(x):
""" This function scraped the required website and extracts the
quickest connection time within given time durations """
#Create a browser object
br = Browser()
# Ignore robots.txt
br.set_handle_robots(False)
# Google demands a user-agent that isn't a robot
br.addheaders = [('User-agent', 'Chrome')]
# Retrieve the Google home page, saving the response
br.open('http://fahrplan.sbb.ch/bin/query.exe/en')
# Select the 6th form
br.select_form(nr=6)
# Assign origin and destination to the o d variables
o = i[0].encode('iso-8859-1')
d = i[1].encode('iso-8859-1')
print 'o-d:', i[0], i[1]
# Enter the text input (This section should be automated to read multiple text input as shown in the question)
br.form["REQ0JourneyStopsS0G"] = o # Origin train station (From)
br.form["REQ0JourneyStopsZ0G"] = d # Destination train station (To)
br.form["REQ0JourneyTime"] = x # Search Time
br.form["date"] = '18.01.17' # Search Date
# Get the search results
br.submit()
#Click the later link three times to get trip times
for _ in xrange(5):
# Looking at some results in link format
for l in br.links(text='Later'):
pass
response = br.follow_link(l)
# get the response from mechanize Browser
soup = BeautifulSoup(br.response().read(), 'lxml', from_encoding="utf-8")
trs = soup.select('table.hfs_overview tr')
connections_times = []
ListOfSearchTimes = []
# Scrape the search results from the resulting table
for tr in trs:
locations = tr.select('td.location')
if len(locations) > 0:
time = tr.select('td.time')[0].contents[0].strip()
ListOfSearchTimes.append(time.encode('latin-1'))
durations = tr.select('td.duration')
# Check that the duration cell is not empty
if len(durations) == 0:
duration = ''
else:
duration = durations[0].contents[0].strip()
# Convert duration time string to minutes
def get_sec(time_str):
h, m = time_str.split(':')
return int(h) * 60 + int(m)
connections_times.append(get_sec(duration))
def group(lst, n):
return zip(*[lst[i::n] for i in range(n)])
arrivals_and_departure_pair = group(ListOfSearchTimes, 2)
#Check that the selected departures for one interval occurs before the departure of the next interval
fmt = '%H:%M'
finalDepartureList = []
for ind, res in arrivals_and_departure_pair:
t1 = datetime.strptime(ind, fmt)
if x == '05:30':
control = datetime.strptime('09:00', fmt)
if x == '09:00':
control = datetime.strptime('12:00', fmt)
if x == '12:00':
control = datetime.strptime('15:00', fmt)
if x == '15:00':
control = datetime.strptime('18:00', fmt)
if x == '18:00':
control = datetime.strptime('21:00', fmt)
if x == '21:00':
control = datetime.strptime('05:30', fmt)
if t1 < control:
finalDepartureList.append(ind)
# Get the the list of connection times for the departures above
fastest_connect = connections_times[:len(finalDepartureList)]
#Get the fastest connections time and catch any error when there is no connection between the OD pairs
try:
best_connect = sorted(fastest_connect)[0]
print 'fastest connection', best_connect
# print duration
except IndexError:
print "No Connection"
#print
#Return the result of the search
if len(fastest_connect) == 0:
return [i[2], i[3], '999999']
else:
return [i[2], i[3], str(best_connect)]
# List of time intervals
times = ['05:30', '09:00', '12:00', '15:00', '18:00', '21:00']
# Write the heading of the output text file
headings = ["from_BAKCode", "to_BAKCode", "interval", "duration"]
f = open("traveltime_rail2rail_2017.txt", "w+")
f.write(','.join(headings))
f.write('\n')
f.close()
# Call the web scraper function
for i in input_data.next():
for index, time in enumerate(times):
result = webscrapper(time)
result.insert(2, str(index+1))
print 'result:', result
print
f = open("traveltime_rail2rail_2017.txt", "a")
f.write(','.join(result[0:4]))
f.write('\n')
f.close()