From 57f73f4c415a897c6331afee1b28da58072e30e7 Mon Sep 17 00:00:00 2001 From: ishan-surana Date: Wed, 3 Jul 2024 01:15:44 +0530 Subject: [PATCH] Minor code changes and build files added --- MetaDataScraper.egg-info/PKG-INFO | 89 +++++++++++++ MetaDataScraper.egg-info/SOURCES.txt | 10 ++ MetaDataScraper.egg-info/dependency_links.txt | 1 + MetaDataScraper.egg-info/requires.txt | 2 + MetaDataScraper.egg-info/top_level.txt | 1 + MetaDataScraper/FacebookScraper.py | 117 +++++++++--------- dist/MetaDataScraper-1.0.2-py3-none-any.whl | Bin 0 -> 13203 bytes dist/metadatascraper-1.0.2.tar.gz | Bin 0 -> 12042 bytes pyproject.toml | 2 +- 9 files changed, 162 insertions(+), 60 deletions(-) create mode 100644 MetaDataScraper.egg-info/PKG-INFO create mode 100644 MetaDataScraper.egg-info/SOURCES.txt create mode 100644 MetaDataScraper.egg-info/dependency_links.txt create mode 100644 MetaDataScraper.egg-info/requires.txt create mode 100644 MetaDataScraper.egg-info/top_level.txt create mode 100644 dist/MetaDataScraper-1.0.2-py3-none-any.whl create mode 100644 dist/metadatascraper-1.0.2.tar.gz diff --git a/MetaDataScraper.egg-info/PKG-INFO b/MetaDataScraper.egg-info/PKG-INFO new file mode 100644 index 0000000..f378879 --- /dev/null +++ b/MetaDataScraper.egg-info/PKG-INFO @@ -0,0 +1,89 @@ +Metadata-Version: 2.1 +Name: MetaDataScraper +Version: 1.0.2 +Summary: A module designed to automate the extraction of follower counts and post details from a public Facebook page. +Author-email: Ishan Surana +Project-URL: Homepage, https://metadatascraper.readthedocs.io/en/latest/ +Classifier: Programming Language :: Python :: 3 +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: Microsoft :: Windows +Requires-Python: >=3.10 +Description-Content-Type: text/markdown +License-File: LICENCE +Requires-Dist: selenium==4.1.0 +Requires-Dist: webdriver-manager==4.0.1 + +[![Licence](https://badgen.net/github/license/ishan-surana/MetaDataScraper?color=DC143C)](https://github.com/ishan-surana/MetaDataScraper/blob/main/LICENCE) [![Python](https://img.shields.io/badge/python-%3E=3.10-slateblue.svg)](https://www.python.org/downloads/release/python-3119/) [![Wheel](https://img.shields.io/badge/wheel-yes-FF00C9.svg)](https://files.pythonhosted.org/packages/02/80/c53d5e8439361c913e23b6345e85e748a7ac7e82e22cb9f7cd9ec77d5d52/MetaDataScraper-1.0.0-py3-none-any.whl) [![Latest](https://badgen.net/github/release/ishan-surana/MetaDataScraper?label=latest+release&color=green)](https://pypi.org/project/MetaDataScraper/1.0.0/) [![Releases](https://badgen.net/github/releases/ishan-surana/MetaDataScraper?color=orange)](https://github.com/ishan-surana/MetaDataScraper/releases) [![Stars](https://badgen.net/github/stars/ishan-surana/MetaDataScraper?color=yellow)](https://github.com/ishan-surana/MetaDataScraper/stargazers) [![Forks](https://badgen.net/github/forks/ishan-surana/MetaDataScraper?color=dark)](https://github.com/ishan-surana/MetaDataScraper/forks) [![Issues](https://badgen.net/github/issues/ishan-surana/MetaDataScraper?color=800000)](https://github.com/ishan-surana/MetaDataScraper/issues) [![PRs](https://badgen.net/github/prs/ishan-surana/MetaDataScraper?color=C71585)](https://github.com/ishan-surana/MetaDataScraper/pulls) [![Last commit](https://badgen.net/github/last-commit/ishan-surana/MetaDataScraper?color=blue)](https://github.com/ishan-surana/MetaDataScraper/commits/main/) ![Downloads](https://img.shields.io/github/downloads/ishan-surana/MetaDataScraper/total) [![Workflow](https://github.com/ishan-surana/MetaDataScraper/actions/workflows/python-publish.yml/badge.svg)](https://github.com/ishan-surana/MetaDataScraper/blob/main/.github/workflows/python-publish.yml) [![PyPI](https://d25lcipzij17d.cloudfront.net/badge.svg?id=py&r=r&ts=1683906897&type=6e&v=1.0.0&x2=0)](https://pypi.org/project/MetaDataScraper/) [![Maintained](https://img.shields.io/badge/maintained-yes-cyan)](https://github.com/ishan-surana/MetaDataScraper/pulse) [![OS](https://img.shields.io/badge/OS-Windows-FF0000)](https://www.microsoft.com/software-download/windows11) [![Documentation Status](https://readthedocs.org/projects/metadatascraper/badge/?version=latest)](https://metadatascraper.readthedocs.io/en/latest/?badge=latest) + +# MetaDataScraper + +MetaDataScraper is a Python package designed to automate the extraction of information like follower counts, and post details & interactions from a public Facebook page, in the form of a list. It uses Selenium WebDriver for web automation and scraping. +The module provides two classes: `LoginlessScraper` and `LoggedInScraper`. The `LoginlessScraper` class does not require any authentication or API keys to scrape the data. However, it has a drawback of being unable to access some Facebook pages. +The `LoggedInScraper` class overcomes this drawback by utilising the credentials of a Facebook account (of user) to login and scrape the data. + +## Installation + +You can install MetaDataScraper using pip: + +``` +pip install MetaDataScraper +``` + +Make sure you have Python 3.x and pip installed. + +## Usage + +To use MetaDataScraper, follow these steps: + +1. Import the `LoginlessScraper` or the `LoggedInScraper` class: + + ```python + from MetaDataScraper import LoginlessScraper, LoggedInScraper + ``` + +2. Initialize the scraper with the Facebook page ID: + + ```python + page_id = "your_target_page_id" + scraper = LoginlessScraper(page_id) + email = "your_facebook_email" + password = "your_facebook_password" + scraper = LoggedInScraper(page_id, email, password) + ``` + +3. Scrape the Facebook page to retrieve information: + + ```python + result = scraper.scrape() + ``` + +4. Access the scraped data from the result dictionary: + + ```python + print(f"Followers: {result['followers']}") + print(f"Post Texts: {result['post_texts']}") + print(f"Post Likes: {result['post_likes']}") + print(f"Post Shares: {result['post_shares']}") + print(f"Is Video: {result['is_video']}") + print(f"Video Links: {result['video_links']}") + ``` + +## Features + +- **Automated Extraction**: Automatically fetches follower counts, post texts, likes, shares, and video links from Facebook pages. +- **Comprehensive Data Retrieval**: Retrieves detailed information about each post, including text content, interaction metrics (likes, shares), and multimedia (e.g., video links). +- **Flexible Handling**: Adapts to diverse post structures and various types of multimedia content present on Facebook pages, like post texts or reels. +- **Enhanced Access with Logged-In Scraper**: Overcomes limitations faced by anonymous scraping (loginless) by utilizing Facebook account credentials for broader page access. +- **Headless Operation**: Executes scraping tasks in headless mode, ensuring seamless and non-intrusive data collection without displaying a browser interface. +- **Scalability**: Supports scaling to handle large volumes of data extraction efficiently, suitable for monitoring multiple Facebook pages simultaneously. +- **Dependency Management**: Utilizes Selenium WebDriver for robust web automation and scraping capabilities, compatible with Python 3.x environments. +- **Ease of Use**: Simplifies the process with straightforward initialization and method calls, facilitating quick integration into existing workflows. + +## Dependencies + +- selenium +- webdriver_manager + +## License + +This project is licensed under the Apache Software License Version 2.0 - see the [LICENSE](https://github.com/ishan-surana/MetaDataScraper/blob/main/LICENCE) file for details. diff --git a/MetaDataScraper.egg-info/SOURCES.txt b/MetaDataScraper.egg-info/SOURCES.txt new file mode 100644 index 0000000..1718c3a --- /dev/null +++ b/MetaDataScraper.egg-info/SOURCES.txt @@ -0,0 +1,10 @@ +LICENCE +README.md +pyproject.toml +MetaDataScraper/FacebookScraper.py +MetaDataScraper/__init__.py +MetaDataScraper.egg-info/PKG-INFO +MetaDataScraper.egg-info/SOURCES.txt +MetaDataScraper.egg-info/dependency_links.txt +MetaDataScraper.egg-info/requires.txt +MetaDataScraper.egg-info/top_level.txt \ No newline at end of file diff --git a/MetaDataScraper.egg-info/dependency_links.txt b/MetaDataScraper.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/MetaDataScraper.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/MetaDataScraper.egg-info/requires.txt b/MetaDataScraper.egg-info/requires.txt new file mode 100644 index 0000000..a45c98f --- /dev/null +++ b/MetaDataScraper.egg-info/requires.txt @@ -0,0 +1,2 @@ +selenium==4.1.0 +webdriver-manager==4.0.1 diff --git a/MetaDataScraper.egg-info/top_level.txt b/MetaDataScraper.egg-info/top_level.txt new file mode 100644 index 0000000..e39f09c --- /dev/null +++ b/MetaDataScraper.egg-info/top_level.txt @@ -0,0 +1 @@ +MetaDataScraper diff --git a/MetaDataScraper/FacebookScraper.py b/MetaDataScraper/FacebookScraper.py index 6d917e6..89ab1da 100644 --- a/MetaDataScraper/FacebookScraper.py +++ b/MetaDataScraper/FacebookScraper.py @@ -1,13 +1,12 @@ +import time +import logging from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By -from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.keys import Keys -from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC -import time -import logging +from webdriver_manager.chrome import ChromeDriverManager logging.getLogger().setLevel(logging.CRITICAL) class LoginlessScraper: @@ -471,7 +470,7 @@ def __scroll_to_top(self): def __get_xpath_constructor(self): """Constructs the XPath for locating posts on the Facebook page.""" - xpath_return_script = r""" + _xpath_return_script = r""" var iterator = document.evaluate('.//*[@aria-label="Like"]', document); var firstelement = iterator.iterateNext() var firstpost = firstelement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement @@ -509,79 +508,79 @@ def __get_xpath_constructor(self): } return xpath_first """ - xpath_constructor = self.driver.execute_script(xpath_return_script) - split_xpath = xpath_constructor.split('[') - split_index = split_xpath.index('1]/div/div/div/div/div/div/div/div/div/div/div') - self.xpath_first = '['.join(split_xpath[:split_index])+'[' - self.xpath_last = '['+'['.join(split_xpath[split_index+1:]) - self.xpath_identifier_addum = ']/div/div/div/div/div/div/div/div/div/div/div' - if len(self.driver.find_element(By.XPATH, xpath_constructor).find_elements(By.TAG_NAME, 'video')): - self.xpath_last = '/'.join(self.xpath_last.split('/')[:3]) + _xpath_constructor = self.driver.execute_script(_xpath_return_script) + _split_xpath = _xpath_constructor.split('[') + _split_index = _split_xpath.index('1]/div/div/div/div/div/div/div/div/div/div/div') + self._xpath_first = '['.join(_split_xpath[:_split_index])+'[' + self._xpath_last = '['+'['.join(_split_xpath[_split_index+1:]) + self._xpath_identifier_addum = ']/div/div/div/div/div/div/div/div/div/div/div' + if len(self.driver.find_element(By.XPATH, _xpath_constructor).find_elements(By.TAG_NAME, 'video')): + self._xpath_last = '/'.join(self._xpath_last.split('/')[:3]) def __extract_post_details(self): """Extracts details of posts including text, likes, shares, and video links.""" - c = 1 - error_count = 0 + _c = 1 + _error_count = 0 while True: - xpath = self.xpath_first + str(c) + self.xpath_identifier_addum + self.xpath_last - if not self.driver.find_elements(By.XPATH, xpath): - error_count += 1 - if error_count < 3: - print('Error extracting post', c, '\b. Count', error_count,'Retrying extraction...', end='\r') + _xpath = self._xpath_first + str(c) + self._xpath_identifier_addum + self._xpath_last + if not self.driver.find_elements(By.XPATH, _xpath): + _error_count += 1 + if _error_count < 3: + print('Error extracting post', _c, '\b. Count', _error_count,'Retrying extraction...', end='\r') time.sleep(5) self.driver.execute_script("window.scrollBy(0, +40);") continue break - error_count = 0 + _error_count = 0 print(" "*100, end='\r') - print("Extracting data of post", c, end='\r') - self.driver.execute_script("arguments[0].scrollIntoView();", self.driver.find_elements(By.XPATH, xpath)[0]) - post_components = self.driver.find_element(By.XPATH, xpath).find_elements(By.XPATH, './*') - if len(post_components) > 2: - post_text = '\n'.join(post_components[2].text.split('\n')) - if post_components[3].text.split('\n')[0] == 'All reactions:': - post_likes = post_components[3].text.split('\n')[1] - if len(post_components[3].text.split('\n')) > 4: - post_shares = post_components[3].text.split('\n')[4].split(' ')[0] - elif len(post_components) > 4 and post_components[4].text.split('\n')[0] == 'All reactions:': - post_likes = post_components[4].text.split('\n')[1] - if len(post_components[4].text.split('\n')) > 4: - post_shares = post_components[4].text.split('\n')[4].split(' ')[0] + print("Extracting data of post", _c, end='\r') + self.driver.execute_script("arguments[0].scrollIntoView();", self.driver.find_elements(By.XPATH, _xpath)[0]) + _post_components = self.driver.find_element(By.XPATH, _xpath).find_elements(By.XPATH, './*') + if len(_post_components) > 2: + _post_text = '\n'.join(_post_components[2].text.split('\n')) + if _post_components[3].text.split('\n')[0] == 'All reactions:': + _post_like = _post_components[3].text.split('\n')[1] + if len(_post_components[3].text.split('\n')) > 4: + _post_share = _post_components[3].text.split('\n')[4].split(' ')[0] + elif len(_post_components) > 4 and _post_components[4].text.split('\n')[0] == 'All reactions:': + _post_like = _post_components[4].text.split('\n')[1] + if len(_post_components[4].text.split('\n')) > 4: + _post_share = _post_components[4].text.split('\n')[4].split(' ')[0] else: - post_likes = 0 - post_shares = 0 - self.post_texts.append(post_text) - self.post_likes.append(post_likes if post_likes else 0) - self.post_shares.append(post_shares if post_shares else 0) - elif len(post_components) == 2: + _post_like = 0 + _post_share = 0 + self.post_texts.append(_post_text) + self.post_likes.append(_post_like if _post_like else 0) + self.post_shares.append(_post_share if _post_share else 0) + elif len(_post_components) == 2: try: - post_shares = post_components[1].find_element(By.XPATH, './/*[@aria-label="Share"]').text + _post_share = _post_components[1].find_element(By.XPATH, './/*[@aria-label="Share"]').text except: - print("Some error occurred while extracting post", c, ". Skipping post...", end='\r') - c += 1 + print("Some error occurred while extracting post", _c, ". Skipping post...", end='\r') + _c += 1 continue - post_likes = post_components[1].find_element(By.XPATH, './/*[@aria-label="Like"]').text - post_shares = post_components[1].find_element(By.XPATH, './/*[@aria-label="Share"]').text + _post_like = _post_components[1].find_element(By.XPATH, './/*[@aria-label="Like"]').text + _post_share = _post_components[1].find_element(By.XPATH, './/*[@aria-label="Share"]').text self.post_texts.append('') - self.post_likes.append(post_likes if post_likes else 0) - self.post_shares.append(post_shares if post_shares else 0) - elif len(post_components) == 1: - post_text = post_components[0].text.split('\n')[0] - post_likes = post_components[0].find_element(By.XPATH, './/*[@aria-label="Like"]').text - post_shares = post_components[0].find_element(By.XPATH, './/*[@aria-label="Share"]').text - self.post_texts.append(post_text) - self.post_likes.append(post_likes if post_likes else 0) - self.post_shares.append(post_shares if post_shares else 0) - if len(self.driver.find_elements(By.XPATH, xpath)[0].find_elements(By.TAG_NAME, 'video')) > 0: - if 'reel' in self.driver.find_elements(By.XPATH, xpath)[0].find_elements(By.TAG_NAME, 'a')[0].get_attribute('href'): - self.video_links.append('https://www.facebook.com' + self.driver.find_elements(By.XPATH, xpath)[0].find_elements(By.TAG_NAME, 'a')[0].get_attribute('href')) + self.post_likes.append(_post_like if _post_like else 0) + self.post_shares.append(_post_share if _post_share else 0) + elif len(_post_components) == 1: + _post_text = _post_components[0].text.split('\n')[0] + _post_like = _post_components[0].find_element(By.XPATH, './/*[@aria-label="Like"]').text + _post_share = _post_components[0].find_element(By.XPATH, './/*[@aria-label="Share"]').text + self.post_texts.append(_post_text) + self.post_likes.append(_post_like if _post_like else 0) + self.post_shares.append(_post_share if _post_share else 0) + if len(self.driver.find_elements(By.XPATH, _xpath)[0].find_elements(By.TAG_NAME, 'video')) > 0: + if 'reel' in self.driver.find_elements(By.XPATH, _xpath)[0].find_elements(By.TAG_NAME, 'a')[0].get_attribute('href'): + self.video_links.append('https://www.facebook.com' + self.driver.find_elements(By.XPATH, _xpath)[0].find_elements(By.TAG_NAME, 'a')[0].get_attribute('href')) else: - self.video_links.append(self.driver.find_elements(By.XPATH, xpath)[0].find_elements(By.TAG_NAME, 'a')[4].get_attribute('href')) + self.video_links.append(self.driver.find_elements(By.XPATH, _xpath)[0].find_elements(By.TAG_NAME, 'a')[4].get_attribute('href')) self.is_video.append(True) else: self.is_video.append(False) self.video_links.append('') - c += 1 + _c += 1 self.post_likes = [int(i) if str(i).isdigit() else 0 for i in self.post_likes] self.post_shares = [int(i) if str(i).isdigit() else 0 for i in self.post_shares] diff --git a/dist/MetaDataScraper-1.0.2-py3-none-any.whl b/dist/MetaDataScraper-1.0.2-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..89e0b70022ba1d88772d4f7624af58b5fcdbc612 GIT binary patch literal 13203 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 U66zoKLI1Uj{iStw#((tw53x|p0{{R3 literal 0 HcmV?d00001 diff --git a/dist/metadatascraper-1.0.2.tar.gz b/dist/metadatascraper-1.0.2.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..79fed33cee08dadb91050d3e0d81c5545973d5bf GIT binary patch literal 12042 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