Skip to content
kaan
uz
dogan

Downloading all images and videos from a private Facebook group

javascript, Facebook3 min read

The Problem

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.

Method#1

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.

Code

Here's the script. Open a text editor and paste it there.

1// This downloads smaller resolutions.
2const imagesContainerSelector = "<selector-here>";
3const downloadInterval = 200; //ms
4const scrollInterval = 100;
5
6const container = document.querySelector(imagesContainerSelector);
7
8// Scroll to bottom
9autoScroll()
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 });
18
19// https://stackoverflow.com/questions/51529332/puppeteer-scroll-down-until-you-cant-anymore
20function 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;
28
29 if (totalHeight >= scrollHeight) {
30 clearInterval(timer);
31 resolve();
32 }
33 }, scrollInterval);
34 });
35}
36
37function 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}
46
47function timeoutPromise(ms) {
48 return new Promise((resolve) => {
49 setTimeout(resolve, ms);
50 });
51}
52
53// https://stackoverflow.com/questions/3916191/download-data-url-file
54function 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}
64
65function getAllLinks(container) {
66 let array = [];
67 for (let i = 1; i <= container.childElementCount; i++) {
68 let link = container
69 .querySelector(`div:nth-of-type(${i}) > div > div > div > div > a > img`)
70 .getAttribute("src");
71 array.push(link);
72 }
73 return array;
74}

Grab the CSS selector of images wrapper

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.

Where to click on the group page

This will take you to the <img> element in the developer tools. But this is not what we want.

img element in the developer tools

Instead collapse the elements until you reach the parent <div> of the whole tile. Should look like this.

parent div in devtools

Right click on the element and copy the CSS selector of the element

copy css selector

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.

Run the script

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.


Method#2

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.

Code

Paste the code into a text editor.

1// Downloads original sizes and keeps video links.
2const nextButtonSelector = '[aria-label="Next photo"]';
3
4const imageSelector = "<paste-here>";
5
6const downloadInterval = 2000; //ms
7
8let nextButton;
9let imageLink;
10let videoLink;
11let prevImageLink;
12let prevVideoLink;
13let i = 1;
14let videoLinks = [];
15console.log("Starting downloads...");
16
17iterateMedia().then(() => {
18 console.log("Here are the links to the videos:");
19 console.log(videoLinks);
20});
21
22function iterateMedia() {
23 return new Promise((resolve) => {
24 let interval = setInterval(() => {
25 nextButton = document.querySelector(nextButtonSelector);
26 if (document.querySelector(imageSelector)) {
27 // is photo
28 imageLink = document.querySelector(imageSelector).getAttribute("src");
29 if (prevImageLink === imageLink) {
30 // end
31 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 video
45 console.log("Video: " + window.location.href);
46 videoLink = videoLinks.push(window.location.href);
47 if (prevVideoLink === videoLink) {
48 // end
49 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}
62
63// https://stackoverflow.com/questions/3916191/download-data-url-file
64function 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}

Grab CSS Selectors

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.

inspect img

On the Developer Tools window right click on the <img> element and click Copy Selector. Paste this to the imageSelector variable in the code.

copy css selector of img

Run the script

Finally copy the whole code to the browser console and paste it. Press enter and run the script.

When crashes

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.

Videos

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.