The Quizlet API has been dead for a while but I want to work on a project that uses their study sets. I spent a while trying to find an API to use but there were many misleading claims; some say the old API works but you need an access token (which there is no way to generate) and some say it doesn't work. I figured out that I'll need to do the extra work without an API and create a backend that gets the HTML from Quizlet and gets the cards from that. I did look for packages such as quizlet-fetc
and quizlet-fetcher
but neither of them worked. This has been a lot more difficult than expected because Quizlet seems to be blocking requests, and Request failed with status code 403
keeps getting logged when I make a request. Below is the code I have so far and I would appreciate any advice.
const express = require('express');
const app = express();
const axios = require('axios');
const cheerio = require('cheerio');
const https = require('https');
const httpProxy = require('http-proxy');
const proxy = httpProxy.createProxyServer();
// Proxy server
proxy.on('error', function(err, req, res) {
console.error(err);
res.writeHead(500, {
'Content-Type': 'text/plain'
});
res.end('Error');
});
// Allow all origins to access this API
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
});
// Fetch the HTML for a Quizlet set
const fetchQuizletSet = async (url) => {
const config = {
headers: {
'Content-Type': 'text/html',
},
httpsAgent: new https.Agent({
rejectUnauthorized: false
})
};
const response = await axios.get(url, config);
return response.data;
};
// Parse the HTML for a Quizlet set and extract the card data
const parseQuizletSet = (html) => {
const $ = cheerio.load(html);
const title = $("h1.PageTitle-heading").text().trim();
const cards = [];
$(".SetPageTerms-term").each((index, element) => {
const front = $(element).find(".SetPageTerm-wordText").text().trim();
const back = $(element).find(".SetPageTerm-definitionText").text().trim();
const image = $(element).find(".SetPageTerm-media img").attr("src");
const audio = $(element)
.find(".SetPageTerm-media audio source")
.attr("src");
cards.push({ front, back, image, audio });
});
return { title, cards };
};
// Define a route to handle Quizlet set requests
app.get('/quizlet-set/:setId', async (req, res) => {
const setId = req.params.setId;
const url = `https://quizlet.com/${setId}`;
try {
const html = await fetchQuizletSet(url);
const data = parseQuizletSet(html);
res.json(data);
} catch (error) {
console.log(error);
res.status(500).send(error.message);
}
});
// Start the server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server listening on port ${PORT}`);
});