Procuring a Student Visa

I received the information about procuring a student visa in mid-October and shelved it immediately. Next week, I thought. But, when I finally read it the next week, it was too late.

The problem: all of the in-person visa appointments with the French consulate were booked clean until my envisioned departure. In order to get a visa, pursuant to "the rules," one must reserve an appointment at least two weeks before arrival so that the consulate has ample time to process the request, stamp the passport, and send it back. In short, I was screwed.

I emailed the consulate directly asking for alternatives. "Nothing sorry."

I emailed my program director. "Keep an eye on the website for cancellations,"she told me. "Cancellations do happen! Just be vigilant in checking."

At this point I was stressed. The consulate offered seven half-hour appointments each weekday, and I was competing against every other traumatized pre-abroad student in the same position for those slots. If any one cancelled I needed to be ready to book my appointment immediately.

I tried the manual approach for a single day. Every couple of minutes I logged into the consulate's portal, selected "Prendre rendez-vous" from the sidebar, and held my breath.

"Aucun créneau disponible." No appointments available.

I needed a better solution. I wanted to receive a notification when a new appointment was available to avoid needing to actively and incessantly refresh the portal. So, I broke out Python and Selenium.

Too Lazy; What's Python? Click here for the conclusion.

from selenium import webdriver

# Allow selenium to run with no visible browser
windowoptions = webdriver.ChromeOptions()options.add_argument('--headless')

chrome = webdriver.Chrome(chrome_options=options)
chrome.get('https://pastel.diplomatie.gouv.fr/...')

I chose Selenium because the page requires user interaction before it shows the desired data: the available appointments. "Selenium automates browsers", and is therefore was a natural choice for this task.

This page was not easy to wrangle. The first oddity is that the page used theHTML tag, which has since been deprecated. Selenium, fortunately, allows navigation into a specific frame.

chrome.switch_to_frame("BODY_WIN")chrome.switch_to_frame("MENU_WIN")

Next, I needed to click the "Prendre rendez-vous" button. However, because the HTML element representing this button did not have an id set, I used an XPath, a unique string identifier for an HTML element on a specific page, to navigate directly to the element.

xpath = '//*[@id="subItems1_0"]/table/tbody/tr[1]/td'
chrome.find_element_by_xpath(xpath).click()

Although the XPath is admittedly strange, it was very easy to create: right click on an HTML element in Chrome Dev Tools and select Copy > Copy XPath.

Then, I needed to agree to the terms and conditions. As I had already agreed to them personally, and because I wrote the script to agree to these terms for me automatically, I figured that this was ok. Admittedly, this is a gray area.

chrome.find_element_by_id("ccg").click() # J'accepte les conditions générales
chrome.find_element_by_id("boutonSuivant").click() # Suivant

Finally, I was ready to see if any appointments were available. The calendar, however, had some quirks.

  1. It appeared a few seconds after the page itself has loaded
  2. It initially displayed the first full month with any available appointments
  3. It only held the dates of available appointments by changing CSS properties.

I let Selenium do some of the heavy lifting by using its WebDriverWait feature.

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC

wait = WebDriverWait(chrome, 10)

# Get the year and month elements, find the text, and strip it
year = wait.until(EC.presence_of_element_located((By.ID, "annee"))).text.strip()
month = wait.until(EC.presence_of_element_located((By.ID, "mois"))).text.strip()

I needed a reservation in 2017, and I preferred December to November.

from mailer, my_email_address import send_email # Custom email module
if year == '2017':
    subject = 'Consulate Appointment Available'
    body = "There is a new appointment available in {0} of {1}. "
    body += "Go check it out!"
    body = body.format(month, year)

    # Send myself an email with the month + year of the available appointment
    send_email(my_email_address, subject, body)

Almost done. I uploaded the script to a server as check_consulate.py and added this to the crontab. The * * * * * means that the script would run every minute – crontab.guru is a great resource for learning about Cron.

* * * * * python ~/check_consulate.py

So...

I sat back and relaxed. I waited. Four days passed and I had not received a single email. I checked the crontab, sent myself test emails, and even continued manually checking the website, though less obsessively, to ensure there were no silly mistakes.

I woke up the next morning to the sound of my phone buzzing. Twenty new emails were in my inbox, from myself. The game was on.

I raced to my desk, flipped open my computer, and opened Chrome. The consulate website came right up as my home page. I clicked Prendre rendez-vous > J'accepte les conditions générales > Suivant. The page loaded and the calendar popped up. January, with no appointments available in December. I was too late.

Fortunately, I had more than one opportunity. Any time somebody cancelled an appointment, which I learned was about every 2-3 days, emails began streaming into my inbox. After three consecutive attempts to snatch an appointment, I finally succeeded on the fourth attempt.

December 23, 2017, 9:00 AM.

Although that appointment sufficed, I could do better. It was only mid-November at the time, and I was worried that with that late-December appointment my passport would not arrive in time for my departure. So I kept at it.

Three days later, I had it. November 30, 2017 11:30 AM.

Ironically, the appointment itself ended up taking five minutes. I brought the thirteen required documents, including my passport, two copies of my bank statement, a letter from my program, proof of residence, and proof of having gone through a three-week application process through CampusFrance (involving mailing a hard copy of all the paperwork to a Washington, DC office) prior to the appointment. I got to the consulate, sat down, was called in immediately, and was out the door five minutes later. I am baffled that the consulate lacks available appointments, although perhaps it is a volume issue.

In either case, three days later I had my passport in hand and was ready to go. And here I am! Ironically, this being a liberal arts program, the French government demanded more of my coding skills than any of my classes here.