— javascript, Facebook — 3 min read
If you want to download your photos in your albums or the albums in your groups Facebook packs them and lets you download easily. However if you have your photos uploaded directly to the group, without an album, you need to download them one by one. Similary for the videos there is no way to export easily. You can't download them directly and need to use some tricks to reach the video file. Here I use some simple browser scripting to automate the process.
This method ends up downloading all images in a shrinked resolution. But is quite fast. And you can't download the videos. Skip to Method#2 if you need full res images or the videos.
Here's the script. Open a text editor and paste it there.
1// This downloads smaller resolutions.2const imagesContainerSelector = "<selector-here>";3const downloadInterval = 200; //ms4const scrollInterval = 100;56const container = document.querySelector(imagesContainerSelector);78// Scroll to bottom9autoScroll()10 .then(() => {11 let links = getAllLinks(container);12 console.log("Will download " + links.length + "files!");13 return downloadAll(links, downloadInterval);14 })15 .then(() => {16 console.log("Downloaded all files successfully!");17 });1819// https://stackoverflow.com/questions/51529332/puppeteer-scroll-down-until-you-cant-anymore20function autoScroll() {21 return new Promise((resolve, reject) => {22 var totalHeight = 0;23 var distance = 50;24 var timer = setInterval(() => {25 var scrollHeight = document.body.scrollHeight;26 window.scrollBy(0, distance);27 totalHeight += distance;2829 if (totalHeight >= scrollHeight) {30 clearInterval(timer);31 resolve();32 }33 }, scrollInterval);34 });35}3637function downloadAll(links, timeout) {38 return new Promise(async (resolve, reject) => {39 for (let i = 0; i < links.length; i++) {40 download(links[i], `${i}.jpg`);41 // console.log(links[i])42 await timeoutPromise(timeout);43 }44 });45}4647function timeoutPromise(ms) {48 return new Promise((resolve) => {49 setTimeout(resolve, ms);50 });51}5253// https://stackoverflow.com/questions/3916191/download-data-url-file54function download(url, filename) {55 fetch(url).then(function (t) {56 return t.blob().then((b) => {57 var a = document.createElement("a");58 a.href = URL.createObjectURL(b);59 a.setAttribute("download", filename);60 a.click();61 });62 });63}6465function getAllLinks(container) {66 let array = [];67 for (let i = 1; i <= container.childElementCount; i++) {68 let link = container69 .querySelector(`div:nth-of-type(${i}) > div > div > div > div > a > img`)70 .getAttribute("src");71 array.push(link);72 }73 return array;74}
Here you need to fill imagesContainerSelector
with the selector of the wrapping div of the photos. In the group's photos page it looks like below. In the tile of photos move the mouse over the first photo and click inspect.
This will take you to the <img>
element in the developer tools. But this is not what we want.
Instead collapse the elements until you reach the parent <div>
of the whole tile. Should look like this.
Right click on the element and copy the CSS selector of the element
Paste the copied selector to imagesContainerSelector
.
1const imagesContainerSelector = "#mount_0_0 > div > div:nth-child(1) >......";
Luckily Facebook keeps the class names when viewing photos and we can exploit this.
That was all we need. Copy the code from your text editor. Go back to the page. Press F12 to open the developer console. Paste the code and press Return. The script will start to scroll until the bottom to load all images and start downloading them.
As stated, this will download images in a lower resolution and won't download videos.
This time we will iterate over images by viewing each image. This will not only show the images but also show the videos in the group. We will download the images in full resolution and keep the video links to download them seperately.
This method uses huge memory and it may crash. This is because of an issue with Facebook Web when you view consecutive photos, each photo increases the app's memory footprint by around 20K and after some N photos it bloats so much that it crashes. I've reported the issue to Facebook and posted a Stackoverflow question and will update if it gets resolved. Until then here's the solution and what to do if it crashes.
Paste the code into a text editor.
1// Downloads original sizes and keeps video links.2const nextButtonSelector = '[aria-label="Next photo"]';34const imageSelector = "<paste-here>";56const downloadInterval = 2000; //ms78let nextButton;9let imageLink;10let videoLink;11let prevImageLink;12let prevVideoLink;13let i = 1;14let videoLinks = [];15console.log("Starting downloads...");1617iterateMedia().then(() => {18 console.log("Here are the links to the videos:");19 console.log(videoLinks);20});2122function iterateMedia() {23 return new Promise((resolve) => {24 let interval = setInterval(() => {25 nextButton = document.querySelector(nextButtonSelector);26 if (document.querySelector(imageSelector)) {27 // is photo28 imageLink = document.querySelector(imageSelector).getAttribute("src");29 if (prevImageLink === imageLink) {30 // end31 clearInterval(interval);32 console.log("Downloaded all images!");33 console.log("Total: " + i + " images");34 console.log("Total: " + videoLinks.length + " videos");35 resolve();36 } else {37 console.log("Downloading image " + i);38 console.log("Photo: " + window.location.href);39 window.localStorage.setItem("lastImageURL", window.location.href);40 download(imageLink, `${i++}.jpg`);41 prevImageLink = imageLink;42 }43 } else {44 // is video45 console.log("Video: " + window.location.href);46 videoLink = videoLinks.push(window.location.href);47 if (prevVideoLink === videoLink) {48 // end49 console.log("Downloaded all images!");50 console.log("Total: " + i + " images");51 console.log("Total: " + videoLinks.length + " videos");52 resolve();53 } else {54 window.localStorage.setItem("videos", videoLinks);55 prevVideoLink = videoLink;56 }57 }58 nextButton.click();59 }, downloadInterval);60 });61}6263// https://stackoverflow.com/questions/3916191/download-data-url-file64function download(url, filename) {65 fetch(url).then(function (t) {66 return t.blob().then((b) => {67 var a = document.createElement("a");68 a.href = URL.createObjectURL(b);69 a.setAttribute("download", filename);70 a.click();71 a.remove();72 });73 });74}
Again, open the media tab of the group or the page. This time open the first item in the gallery. Right click on the image and select inspect.
On the Developer Tools window right click on the <img>
element and click Copy Selector. Paste this to the imageSelector
variable in the code.
Finally copy the whole code to the browser console and paste it. Press enter and run the script.
Now, if you're lucky or don't have too many images, the script will execute successfully. Usually the page will crash after a while. For that we keep the lastImage
and videos
in the Local Storage of the page. You can find it in the Application tab after pressing F12 and under www.facebook.com
. Make sure you save those before starting new.
Now, you can check the last image it downloaded, open the next one in the group, and before pasting just change the below line to the lastImage
number + 1 instead of 1
. So if lastImage
was 521
1let i = 522;
Make sure you also saved videos
from the Local Storage into a file called videos.csv
and run the script. Repeat if the page crashes again. Append all videos into the same file after each crash.
If all goes well, you should have a comma-seperated list of video links stored in videos
Local Storage variable. Save them into a file called videos.csv
. The file should look like this (but longer):
1https://www.facebook.com/268490061405/videos/403980490753831,https://www.facebook.com/268490061405/videos/388733212392115,https://www.facebook.com/268490061405/videos/2784268961848584,https://www.facebook.com/268490061405/videos/1035479846953596
We will use the good old youtube-dl to download the videos. As of youtube-dl
update 12.12.2020 private video downloads for Facebook work, but this can break anytime. For public videos you don't need to provide username and password.
Execute the following command to download all videos. You need to turn off two factor authentication for youtube-dl
to log-in.
1awk '{printf $0 "\n"}' RS="," videos.csv | xargs -p youtube-dl -u <your-fb-username> -p <your-fb-password>
This will take some time depending on how many videos you have.
And there you go! You should now have a complete back-up of a Facebook group/page.