├── instabot ├── top_5_hashtags_do_dia.txt ├── scrapy_twitter.py ├── __main__.py ├── locale │ ├── en.yml │ └── pt.yml ├── bot_inputs.py ├── modos_bot.py └── bot.py ├── requirements.txt ├── .github ├── workflows │ └── pylint.yml └── FUNDING.yml ├── LICENSE ├── README.md └── .gitignore /instabot/top_5_hashtags_do_dia.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drbuche/InstaBot/HEAD/requirements.txt -------------------------------------------------------------------------------- /.github/workflows/pylint.yml: -------------------------------------------------------------------------------- 1 | name: Pylint 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Set up Python 3.8 13 | uses: actions/setup-python@v1 14 | with: 15 | python-version: 3.8 16 | - name: Install dependencies 17 | run: | 18 | python -m pip install --upgrade pip 19 | pip install pylint 20 | - name: Analysing the code with pylint 21 | working-directory: instabot 22 | continue-on-error: true 23 | run: | 24 | pylint `ls -R|grep .py$|xargs` 25 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: [ 13 | 'https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=slsulek%40gmail.com&item_name=instabot+donation¤cy_code=BRL&source=url', 14 | ] 15 | -------------------------------------------------------------------------------- /instabot/scrapy_twitter.py: -------------------------------------------------------------------------------- 1 | import scrapy 2 | import os 3 | 4 | class TwitterScrapy(scrapy.Spider): 5 | name = 'twitter' 6 | start_urls = ['https://trends24.in/brazil/'] 7 | hashtags_dia_lista = [] 8 | 9 | def parse(self, response): 10 | hashtags_trends_response = response.xpath('//body//div//ol//li//a/text()').getall() 11 | 12 | for loop in range(5): 13 | hashtags_trend_bruto = hashtags_trends_response[loop].replace('#', '').replace(' ', '') 14 | hashtags_trend = "".join(hashtags_trend_bruto) 15 | self.hashtags_dia_lista.append(hashtags_trend) 16 | yield {'top_5': hashtags_trend} 17 | 18 | hashtags_dia = open(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'top_5_hashtags_do_dia.txt'), "w") 19 | hashtags_dia.writelines(','.join(self.hashtags_dia_lista)) 20 | hashtags_dia.close() 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Fábio Augusto Buche Barros 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /instabot/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from modos_bot import * 3 | from bot_inputs import * 4 | import os 5 | import i18n 6 | 7 | if __name__ == "__main__": 8 | # Software - Lenguage Selection 9 | inicio = Inputs() 10 | i18n.set('locale', inicio.language) 11 | 12 | # Hashtags que deseja pesquisar 13 | hashtags = ['hashtag1', 'hashtag2', 'hashtag3'] 14 | perfis = ["perfil1", "perfil2", "perfil3"] 15 | local = ['https://www.instagram.com/explore/locations/1964119/goiabeiras-shopping/', 'https://www.instagram.com/explore/locations/215855667/chapada-dos-guimaraes/'] 16 | 17 | 18 | # Combinação de palavras para comentário aleatório -> (a + b) ou (a + c) ou (b + c) 19 | primeira_palavra = ['Nossa! ', 'WOW! ', 'Sério isso? ', 'CARAMBA! '] 20 | complemento = ['Que incrível!', 'Que top!', 'Que show!', 'Que TUDO!'] 21 | emoji = ['💙️', '🥰', '🔥', '💥'] 22 | 23 | print('--------------------------------------------------------------------------------\n') 24 | navegador = inicio.webdriver() 25 | print('--------------------------------------------------------------------------------\n') 26 | username, password = inicio.login_pass() 27 | print('\n--------------------------------------------------------------------------------\n') 28 | tipo_busca = inicio.bot_fazer() 29 | print('\n--------------------------------------------------------------------------------\n') 30 | modo_bot = inicio.hash_seguidores_local(hashtags, local) 31 | print('\n--------------------------------------------------------------------------------\n') 32 | linguagem = inicio.linguagem() 33 | print('\n--------------------------------------------------------------------------------\n') 34 | 35 | eval(modo_bot) 36 | -------------------------------------------------------------------------------- /instabot/locale/en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | invalid_value: Invalid Value 3 | webdriver_input: | 4 | What's your Webdriver 5 | Chrome: Type 1. 6 | Firefox: Type 2. 7 | username_input: Username? 8 | pass_input: Password? 9 | bot_fazer: | 10 | To just Comment: Type 1 (It takes 6 to 10 minutes between each comments). 11 | To just Like: Type 2 (One like every 1-2 min.) 12 | To Comment and Like: Type 3 (Every 10 likes 1 comment). 13 | hash_seguidores: | 14 | To work on Hashtags: Type 1. 15 | To work on Followers of a profile: Type 2. 16 | To work on Locations: Type 3. 17 | hash_local_trends: | 18 | To work on custom hashtags: Type 1. 19 | To work with the top 5 # in brazil/eua (last minute on twitter): Type 2. 20 | suas_hash: "His hashtags are: " 21 | top_hash: "The top 5 hashtags are:" 22 | seus_locais: "Locations are:" 23 | seus_perfis: "Profiles are:" 24 | linguagem: | 25 | What is the language of your browser? 26 | Type 1 for English 27 | Type 2 for Portuguese 28 | com_hashtags_inicia: "started on: " 29 | num_com_hashtags_coletadas: "Number of Pics collected:" 30 | com_hashtags_finalizada: "ended in: " 31 | lista_com_hashtags_acabou: "The list is over! :" 32 | sem_hashtags_inicia: "Profile: " 33 | sem_hashtags_numero_seg: "Number of Followers collected:" 34 | lista_sem_hashtags_acabou: "The list is over! :" 35 | lista_com_locais_acabou: " - The photo list is over!" 36 | fotos_faltando_hash: "|Nº - Pics Left |Nº - Current Pics| " 37 | seguidores_faltando: "|Nº - Profiles Left | Nº - Profiles Visited | Nº - Current Pics |" 38 | deu_ruim: "\nA Something is wrong! For security, the program was closed.\n" -------------------------------------------------------------------------------- /instabot/locale/pt.yml: -------------------------------------------------------------------------------- 1 | pt: 2 | invalid_value: Valor Incorreto 3 | webdriver_input: | 4 | Qual seu Webdriver? 5 | Chrome: digite 1. 6 | Firefox: digite 2. 7 | username_input: Qual o usuário? 8 | pass_input: Qual a senha? 9 | bot_fazer: | 10 | Para apenas comentar: Digite 1 (Leva de 6-10 min entre comentários). 11 | Para apenas dar like: Digite 2 (1 like a cada 1-2 min). 12 | Para comentar e dar like: Digite 3 (A cada 10 likes 1 comentário). 13 | hash_seguidores: | 14 | Para trabalhar em Hashtags: Digite 1. 15 | Para trabalhar em Seguidores de um perfil: Digite 2. 16 | Para trabalhar em Locais: Digite 3. 17 | hash_local_trends: | 18 | Para trabalhar em hashtags personalizadas: Digite 1. 19 | Para trabalhar com as top 5 # do brasil (da última hora no twitter): Digite 2. 20 | suas_hash: "Suas hashtags são:" 21 | top_hash: "As top 5 hashtags são:" 22 | seus_locais: "Seus Locais são:" 23 | seus_perfis: "Seus Perfis são:" 24 | linguagem: | 25 | Qual o idioma do seu navegador? 26 | Digite 1 para Inglês 27 | Digite 2 para Português 28 | com_hashtags_inicia: "iniciada em: " 29 | num_com_hashtags_coletadas: "Número de Fotos coletadas:" 30 | com_hashtags_finalizada: "finalizada em: " 31 | lista_com_hashtags_acabou: "A lista de Hashtags acabou! : " 32 | lista_com_locais_acabou: " - A lista de fotos acabou! : " 33 | sem_hashtags_inicia: "Perfil: " 34 | sem_hashtags_numero_seg: "Número de seguidores coletados:" 35 | lista_sem_hashtags_acabou: "A lista de perfis acabou! : " 36 | fotos_faltando_hash: "|Nº - Fotos Faltando |Nº - Fotos Atual | " 37 | seguidores_faltando: "|Nº - Perfis Faltando | Nº - Perfis Visitados | Nº - Fotos Atual |" 38 | deu_ruim: "\nAlgo está errado! Por segurança, o programa foi finalizado.\n" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ![N|Solid](https://i.imgur.com/33xjIQf.jpg) 3 | 4 | # InstaBot - Python: Selenium + Scrapy 5 | 6 | ### Guide Language / Idioma do Guia: 7 | - [English Guide](https://github.com/drbuche/InstaBot/wiki#english-guide) 8 | - [Guia em Português](https://github.com/drbuche/InstaBot/wiki#guia-em-portugu%C3%AAs) 9 | 10 | ***The bot works 100% in Portuguese and English!*** 11 | ***O bot funciona 100% em Português e Ingles!*** 12 | 13 | ### What he does? 14 | 15 | Bot to automate likes and comments using Selenium and Scrapy! 16 | 17 | - [x] Just Like photos. 18 | - [x] Just Comment on photos. 19 | - [x] Like and Comment on photos. 20 | - [x] It has 3 search modes. 21 | - Hashtags. 22 | - Followers of a profile. 23 | - Locations. 24 | - [x] It has a sub-mode that searches for Twitter Trending Topics and Hashtags in Brazil.(Improving) 25 | 26 | *I intend to add more features and build a graphical interface in the future!* 27 | 28 | # Bot Performance! 29 | 30 | - I created an account, put some photos and followed some groups of bands, games, etc ... 31 | 32 | - So far, with 8 days of use, the account has left ZERO and has: 33 | - **205 Followers.** 34 | - **711 Likes on the photos.** 35 | - **30 comments on photos.** 36 | - **More than 30 DM's.** 37 | 38 | 39 |

40 | 41 |

42 | 43 | - Bot has been running on this account since September 11th. (8 days of use) 44 | - I'm running the Bot for 6-8 hours a day. 45 | - Use in **"comment + like"** mode on **profile followers**. 46 | - To interact with the last 120 followers of the profile, I look for profiles that have an average of 15 to 50 thousand followers. (profiles with more than 100,000 followers are good, but tend to have a lot of fake accounts ...) 47 | - I select profiles that have a niche similar to my instagram account profile. 48 | 49 | 50 | ![N|Solid](https://i.imgur.com/IjinG9z.jpg) 51 | 52 | 53 |

54 | 55 |

56 | 57 |

58 | 59 |

60 | 61 | --- 62 | 63 | 64 | ## O que é? 65 | 66 | Bot para automatizar curtidas e comentários usando Selenium e Scrapy! 67 | 68 | - [x] Apenas Curtir fotos. 69 | - [x] Apenas Comentar em fotos. 70 | - [x] Curtir e Comentar em fotos. 71 | - [x] Possui 3 modos de busca. 72 | - Hashtags. 73 | - Seguidores de um perfil. 74 | - Locais. 75 | - [x] Possui um sub-modo que busca por Trending Topics e Hashtags do twitter no Brasil. 76 | 77 | *Pretendo adicionar mais funcionalidades e construir uma interface gráfica futuramente!* 78 | 79 | # Desempenho do Bot! 80 | 81 | - Criei uma conta do zero, coloquei algumas fotos e segui alguns grupos de bandas, jogos, etc... 82 | 83 | - Até o momento, com 8 dias de uso, a conta saiu do ZERO e está com: 84 | - **205 Seguidores.** 85 | - **711 Likes nas fotos.** 86 | - **30 comentários em fotos.** 87 | - **Mais de 30 mensagens no Direct.** 88 | 89 | 90 |

91 | 92 |

93 | 94 | - O Bot está rodando nesta conta desde o dia 11 de setembro. (8 dias de uso) 95 | - Estou rodando o Bot de 6-8h por dia. 96 | - Utilizo no modo **"comentar + curtir"** em **seguidores de perfis**. 97 | - Para interagir com os últimos 120 seguidores do perfil, procuro por perfis que tem em média 15 a 50 mil seguidores. (perfis com mais de 100 mil seguidores são bons, mas costumam ter muitas contas fake...) 98 | - Seleciono perfis que tem um nicho parecido com o perfil da minha conta do instagram. 99 | 100 | 101 | ![N|Solid](https://i.imgur.com/IjinG9z.jpg) 102 | 103 | 104 |

105 | 106 |

107 | 108 |

109 | 110 |

111 | -------------------------------------------------------------------------------- /instabot/bot_inputs.py: -------------------------------------------------------------------------------- 1 | from getpass import getpass 2 | import i18n 3 | import os 4 | 5 | 6 | class Inputs: 7 | 8 | def __init__(self): 9 | i18n.load_path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'locale')) 10 | i18n.set('filename_format', '{locale}.{format}') 11 | i18n.set('fallback', 'en') 12 | self.grupo_nome_local_limpo = [] 13 | 14 | while True: 15 | print('-----------------------------Software Language----------------------------------') 16 | language_selection = input("For english: Type 1\n" 17 | "Para Português: Digite 2\n") 18 | 19 | if language_selection == '1': 20 | self.language = 'en' 21 | return 22 | 23 | elif language_selection == '2': 24 | self.language = 'pt' 25 | return 26 | 27 | else: 28 | print(i18n.t('invalid_value')) 29 | 30 | print('--------------------------------------------------------------------------------') 31 | 32 | @staticmethod 33 | def webdriver(): 34 | while True: 35 | driver = input(i18n.t('webdriver_input')) 36 | if driver == '1': 37 | return 'webdriver.Chrome()' 38 | elif driver == '2': 39 | return 'webdriver.Firefox()' 40 | else: 41 | print(i18n.t('invalid_value')) 42 | 43 | @staticmethod 44 | def login_pass(): 45 | username = input(i18n.t('username_input')) 46 | password = getpass(prompt=i18n.t('pass_input')) 47 | return username, password 48 | 49 | @staticmethod 50 | def bot_fazer(): 51 | while True: 52 | seleciona_tarefa = input(i18n.t('bot_fazer')) 53 | if seleciona_tarefa == '1': 54 | return 'comentar_fotos(primeira_palavra, complemento, emoji)' 55 | elif seleciona_tarefa == '2': 56 | return 'like_foto()' 57 | elif seleciona_tarefa == '3': 58 | return 'like_comentar(primeira_palavra, complemento, emoji)' 59 | else: 60 | print(i18n.t('invalid_value')) 61 | 62 | @staticmethod 63 | def hash_local_trends(hashtags): 64 | while True: 65 | local_trends = input(i18n.t('hash_local_trends')) 66 | if local_trends == '1': 67 | return print(i18n.t('suas_hash') + str(hashtags)) 68 | elif local_trends == '2': 69 | hashtags.clear() 70 | # Futuro auto execute do scrapy para diferentes SO 71 | top_5_hashtags = open(os.path.join(os.path.dirname(os.path.abspath(__file__)), 72 | 'top_5_hashtags_do_dia.txt'), "r") 73 | hashtags_bruta = top_5_hashtags.readlines() 74 | hashtags_lista = str(hashtags_bruta).replace('\'', '').replace('[', '').replace(']', '').split(',') 75 | for hashtag in hashtags_lista: 76 | hashtags.append(hashtag.strip()) 77 | print(i18n.t('top_hash') + str(hashtags)) 78 | return top_5_hashtags.close() 79 | 80 | else: 81 | print(i18n.t('invalid_value')) 82 | 83 | @staticmethod 84 | def limpar_link_local(local, grupo_nome_local_limpo): 85 | for link in local: 86 | link_limpo = link.replace('https://www.instagram.com/explore/locations/', '').split('/') 87 | grupo_nome_local_limpo.append(link_limpo[1]) 88 | 89 | @staticmethod 90 | def linguagem(): 91 | while True: 92 | lingua = input(i18n.t('linguagem')) 93 | if lingua == '1' or lingua == '2': 94 | return lingua 95 | else: 96 | print(i18n.t('invalid_value')) 97 | 98 | def hash_seguidores_local(self, hashtags, local): 99 | while True: 100 | seleciona_tarefa = input(i18n.t('hash_seguidores')) 101 | if seleciona_tarefa == '1': 102 | print('\n--------------------------------------------------------------------------------\n') 103 | self.hash_local_trends(hashtags) 104 | print('\n--------------------------------------------------------------------------------\n') 105 | return 'com_hashtags(hashtags, tipo_busca, primeira_palavra, complemento, ' \ 106 | 'emoji, username, password, modo_bot, linguagem, navegador, inicio)' 107 | elif seleciona_tarefa == '2': 108 | return 'com_perfil(perfis, tipo_busca, primeira_palavra, complemento, ' \ 109 | 'emoji, username, password, modo_bot, linguagem, navegador, inicio)' 110 | elif seleciona_tarefa == '3': 111 | 112 | print('\n--------------------------------------------------------------------------------\n') 113 | self.limpar_link_local(local, self.grupo_nome_local_limpo) 114 | print(i18n.t('seus_locais') + str(self.grupo_nome_local_limpo)) 115 | return 'com_local(local, tipo_busca, primeira_palavra, complemento, ' \ 116 | 'emoji, username, password, modo_bot, linguagem, navegador, inicio)' 117 | else: 118 | print(i18n.t('invalid_value')) 119 | -------------------------------------------------------------------------------- /instabot/modos_bot.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from bot import * 3 | import i18n 4 | 5 | 6 | def com_hashtags(hashtags, tipo_busca, primeira_palavra, complemento, 7 | emoji, username, password, modo_bot, linguagem, navegador, inicio): 8 | instagram = InstagramBot(username, password, modo_bot, linguagem, navegador, inicio) 9 | time.sleep(5) 10 | instagram.login() 11 | while True: 12 | if len(hashtags) != 0: 13 | try: 14 | tag = random.choice(hashtags) 15 | print('--------------------------------------------------------------------------------') 16 | print(f'\nHashtag: #{tag} ' + i18n.t('com_hashtags_inicia') + ' ' + 17 | datetime.now().strftime("%Y-%m-%d %H:%M:%S") + '\n') 18 | instagram.selecionar_fotos_hashtags(tag) 19 | print(f'{i18n.t("num_com_hashtags_coletadas")} {len(instagram.pic_hrefs)} \n') 20 | instagram.tabela_stdout() 21 | eval('instagram.' + tipo_busca) 22 | print('\n----------------------------------------\n') 23 | print(f'\nHashtag: #{tag} ' + i18n.t('com_hashtags_finalizada') + 24 | datetime.now().strftime("%Y-%m-%d %H:%M:%S")) 25 | print('\n--------------------------------------------------------------------------------\n') 26 | hashtags.remove(tag) 27 | instagram.limpar_urefs() 28 | except Exception as e: 29 | print(f'\n {e} \n') 30 | instagram.deu_ruim() 31 | else: 32 | instagram.close_browser() 33 | print(i18n.t('lista_com_hashtags_acabou') + datetime.now().strftime("%Y-%m-%d %H:%M:%S")) 34 | sys.exit() 35 | 36 | 37 | def com_local(local, tipo_busca, primeira_palavra, complemento, 38 | emoji, username, password, modo_bot, linguagem, navegador, inicio): 39 | instagram = InstagramBot(username, password, modo_bot, linguagem, navegador, inicio) 40 | time.sleep(5) 41 | instagram.login() 42 | while True: 43 | if len(local) != 0: 44 | try: 45 | tag = random.choice(local) 46 | regex_tag = tag.replace('https://www.instagram.com/explore/locations/', '').split('/') 47 | print('--------------------------------------------------------------------------------') 48 | print(f'\nLocal: 📍{regex_tag[1]} ' + i18n.t('com_hashtags_inicia') + ' ' + 49 | datetime.now().strftime("%Y-%m-%d %H:%M:%S") + '\n') 50 | instagram.selecionar_fotos_local(tag) 51 | print(f'{i18n.t("num_com_hashtags_coletadas")} {len(instagram.pic_hrefs)} \n') 52 | instagram.tabela_stdout() 53 | eval('instagram.' + tipo_busca) 54 | print('\n----------------------------------------\n') 55 | print(f'\nLocal: 📍{regex_tag[1]} ' + i18n.t('lista_com_locais_acabou') + 56 | datetime.now().strftime("%Y-%m-%d %H:%M:%S")) 57 | print('\n--------------------------------------------------------------------------------\n') 58 | local.remove(tag) 59 | instagram.limpar_urefs() 60 | except Exception as e: 61 | print(f'\n {e} \n') 62 | instagram.deu_ruim() 63 | else: 64 | instagram.close_browser() 65 | print(i18n.t('lista_com_hashtags_acabou') + datetime.now().strftime("%Y-%m-%d %H:%M:%S")) 66 | sys.exit() 67 | 68 | 69 | def com_perfil(perfis, tipo_busca, primeira_palavra, complemento, 70 | emoji, username, password, modo_bot, linguagem, navegador, inicio): 71 | instagram = InstagramBot(username, password, modo_bot, linguagem, navegador, inicio) 72 | time.sleep(5) 73 | instagram.login() 74 | while True: 75 | if len(perfis) != 0: 76 | try: 77 | tag = random.choice(perfis) 78 | print('--------------------------------------------------------------------------------') 79 | print('\n' + i18n.t("sem_hashtags_inicia") + f'@{tag} ' + i18n.t("com_hashtags_inicia") 80 | + datetime.now().strftime("%Y-%m-%d %H:%M:%S")) 81 | instagram.listar_perfis_do_perfil(tag) 82 | print(f'{i18n.t("sem_hashtags_numero_seg")} {len(instagram.seguidores_perfil)}.\n') 83 | instagram.tabela_stdout() 84 | while len(instagram.seguidores_perfil) != 0: 85 | time.sleep(20) 86 | instagram.selecionar_fotos_perfil() 87 | eval('instagram.' + tipo_busca) 88 | instagram.limpar_urefs() 89 | print('\n-------------------------------------------------------------------\n') 90 | print('\n' + i18n.t("sem_hashtags_inicia") + f' @{tag} ' + i18n.t("com_hashtags_finalizada") 91 | + datetime.now().strftime("%Y-%m-%d %H:%M:%S")) 92 | print('\n--------------------------------------------------------------------------------\n') 93 | perfis.remove(tag) 94 | except Exception as e: 95 | print(f'\n {e} \n') 96 | instagram.deu_ruim() 97 | else: 98 | instagram.close_browser() 99 | print(i18n.t('lista_sem_hashtags_acabou') + datetime.now().strftime("%Y-%m-%d %H:%M:%S")) 100 | sys.exit() 101 | 102 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/windows,linux,macos,python,pycharm+all,visualstudiocode,virtualenv 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=windows,linux,macos,python,pycharm+all,visualstudiocode,virtualenv 4 | 5 | ### Linux ### 6 | *~ 7 | 8 | # temporary files which can be created if a process still has a handle open of a deleted file 9 | .fuse_hidden* 10 | 11 | # KDE directory preferences 12 | .directory 13 | 14 | # Linux trash folder which might appear on any partition or disk 15 | .Trash-* 16 | 17 | # .nfs files are created when an open file is removed but is still being accessed 18 | .nfs* 19 | 20 | ### macOS ### 21 | # General 22 | .DS_Store 23 | .AppleDouble 24 | .LSOverride 25 | 26 | # Icon must end with two \r 27 | Icon 28 | 29 | # Thumbnails 30 | ._* 31 | 32 | # Files that might appear in the root of a volume 33 | .DocumentRevisions-V100 34 | .fseventsd 35 | .Spotlight-V100 36 | .TemporaryItems 37 | .Trashes 38 | .VolumeIcon.icns 39 | .com.apple.timemachine.donotpresent 40 | 41 | # Directories potentially created on remote AFP share 42 | .AppleDB 43 | .AppleDesktop 44 | Network Trash Folder 45 | Temporary Items 46 | .apdisk 47 | 48 | ### PyCharm+all ### 49 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 50 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 51 | 52 | # User-specific stuff 53 | .idea/**/workspace.xml 54 | .idea/**/tasks.xml 55 | .idea/**/usage.statistics.xml 56 | .idea/**/dictionaries 57 | .idea/**/shelf 58 | 59 | # Generated files 60 | .idea/**/contentModel.xml 61 | 62 | # Sensitive or high-churn files 63 | .idea/**/dataSources/ 64 | .idea/**/dataSources.ids 65 | .idea/**/dataSources.local.xml 66 | .idea/**/sqlDataSources.xml 67 | .idea/**/dynamic.xml 68 | .idea/**/uiDesigner.xml 69 | .idea/**/dbnavigator.xml 70 | 71 | # Gradle 72 | .idea/**/gradle.xml 73 | .idea/**/libraries 74 | 75 | # Gradle and Maven with auto-import 76 | # When using Gradle or Maven with auto-import, you should exclude module files, 77 | # since they will be recreated, and may cause churn. Uncomment if using 78 | # auto-import. 79 | # .idea/artifacts 80 | # .idea/compiler.xml 81 | # .idea/jarRepositories.xml 82 | # .idea/modules.xml 83 | # .idea/*.iml 84 | # .idea/modules 85 | # *.iml 86 | # *.ipr 87 | 88 | # CMake 89 | cmake-build-*/ 90 | 91 | # Mongo Explorer plugin 92 | .idea/**/mongoSettings.xml 93 | 94 | # File-based project format 95 | *.iws 96 | 97 | # IntelliJ 98 | out/ 99 | 100 | # mpeltonen/sbt-idea plugin 101 | .idea_modules/ 102 | 103 | # JIRA plugin 104 | atlassian-ide-plugin.xml 105 | 106 | # Cursive Clojure plugin 107 | .idea/replstate.xml 108 | 109 | # Crashlytics plugin (for Android Studio and IntelliJ) 110 | com_crashlytics_export_strings.xml 111 | crashlytics.properties 112 | crashlytics-build.properties 113 | fabric.properties 114 | 115 | # Editor-based Rest Client 116 | .idea/httpRequests 117 | 118 | # Android studio 3.1+ serialized cache file 119 | .idea/caches/build_file_checksums.ser 120 | 121 | ### PyCharm+all Patch ### 122 | # Ignores the whole .idea folder and all .iml files 123 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 124 | 125 | .idea/ 126 | 127 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 128 | 129 | *.iml 130 | modules.xml 131 | .idea/misc.xml 132 | *.ipr 133 | 134 | # Sonarlint plugin 135 | .idea/sonarlint 136 | 137 | ### Python ### 138 | # Byte-compiled / optimized / DLL files 139 | __pycache__/ 140 | *.py[cod] 141 | *$py.class 142 | 143 | # C extensions 144 | *.so 145 | 146 | # Distribution / packaging 147 | .Python 148 | build/ 149 | develop-eggs/ 150 | dist/ 151 | downloads/ 152 | eggs/ 153 | .eggs/ 154 | lib/ 155 | lib64/ 156 | parts/ 157 | sdist/ 158 | var/ 159 | wheels/ 160 | pip-wheel-metadata/ 161 | share/python-wheels/ 162 | *.egg-info/ 163 | .installed.cfg 164 | *.egg 165 | MANIFEST 166 | 167 | # PyInstaller 168 | # Usually these files are written by a python script from a template 169 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 170 | *.manifest 171 | *.spec 172 | 173 | # Installer logs 174 | pip-log.txt 175 | pip-delete-this-directory.txt 176 | 177 | # Unit test / coverage reports 178 | htmlcov/ 179 | .tox/ 180 | .nox/ 181 | .coverage 182 | .coverage.* 183 | .cache 184 | nosetests.xml 185 | coverage.xml 186 | *.cover 187 | *.py,cover 188 | .hypothesis/ 189 | .pytest_cache/ 190 | pytestdebug.log 191 | 192 | # Translations 193 | *.mo 194 | *.pot 195 | 196 | # Django stuff: 197 | *.log 198 | local_settings.py 199 | db.sqlite3 200 | db.sqlite3-journal 201 | 202 | # Flask stuff: 203 | instance/ 204 | .webassets-cache 205 | 206 | # Scrapy stuff: 207 | .scrapy 208 | 209 | # Sphinx documentation 210 | docs/_build/ 211 | doc/_build/ 212 | 213 | # PyBuilder 214 | target/ 215 | 216 | # Jupyter Notebook 217 | .ipynb_checkpoints 218 | 219 | # IPython 220 | profile_default/ 221 | ipython_config.py 222 | 223 | # pyenv 224 | .python-version 225 | 226 | # pipenv 227 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 228 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 229 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 230 | # install all needed dependencies. 231 | #Pipfile.lock 232 | 233 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 234 | __pypackages__/ 235 | 236 | # Celery stuff 237 | celerybeat-schedule 238 | celerybeat.pid 239 | 240 | # SageMath parsed files 241 | *.sage.py 242 | 243 | # Environments 244 | .env 245 | .venv 246 | env/ 247 | venv/ 248 | ENV/ 249 | env.bak/ 250 | venv.bak/ 251 | 252 | # Spyder project settings 253 | .spyderproject 254 | .spyproject 255 | 256 | # Rope project settings 257 | .ropeproject 258 | 259 | # mkdocs documentation 260 | /site 261 | 262 | # mypy 263 | .mypy_cache/ 264 | .dmypy.json 265 | dmypy.json 266 | 267 | # Pyre type checker 268 | .pyre/ 269 | 270 | # pytype static type analyzer 271 | .pytype/ 272 | 273 | ### VirtualEnv ### 274 | # Virtualenv 275 | # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ 276 | [Bb]in 277 | [Ii]nclude 278 | [Ll]ib 279 | [Ll]ib64 280 | [Ll]ocal 281 | [Ss]cripts 282 | pyvenv.cfg 283 | pip-selfcheck.json 284 | 285 | ### VisualStudioCode ### 286 | .vscode/* 287 | !.vscode/settings.json 288 | !.vscode/tasks.json 289 | !.vscode/launch.json 290 | !.vscode/extensions.json 291 | *.code-workspace 292 | 293 | ### VisualStudioCode Patch ### 294 | # Ignore all local history of files 295 | .history 296 | 297 | ### Windows ### 298 | # Windows thumbnail cache files 299 | Thumbs.db 300 | Thumbs.db:encryptable 301 | ehthumbs.db 302 | ehthumbs_vista.db 303 | 304 | # Dump file 305 | *.stackdump 306 | 307 | # Folder config file 308 | [Dd]esktop.ini 309 | 310 | # Recycle Bin used on file shares 311 | $RECYCLE.BIN/ 312 | 313 | # Windows Installer files 314 | *.cab 315 | *.msi 316 | *.msix 317 | *.msm 318 | *.msp 319 | 320 | # Windows shortcuts 321 | *.lnk 322 | 323 | # End of https://www.toptal.com/developers/gitignore/api/windows,linux,macos,python,pycharm+all,visualstudiocode,virtualenv -------------------------------------------------------------------------------- /instabot/bot.py: -------------------------------------------------------------------------------- 1 | from selenium import webdriver 2 | from selenium.webdriver.common.keys import Keys 3 | import time 4 | import random 5 | import sys 6 | import i18n 7 | 8 | 9 | class InstagramBot: 10 | seguidores_perfil = [] 11 | pic_hrefs = [] 12 | numero_perfis_atual = 0 13 | numero_fotos = 0 14 | contador_para_comentar = 1 15 | 16 | def __init__(self, username, password, stdout, linguagem, navegador, inicio): 17 | self.username = username 18 | self.password = password 19 | self.stdout = stdout 20 | self.driver = eval(navegador) 21 | self.linguagem = linguagem 22 | self.inicio = inicio 23 | if linguagem == '1': 24 | self.like = 'Like' 25 | self.post = 'Post' 26 | self.unlike = 'Unlike' 27 | else: 28 | self.like = 'Curtir' 29 | self.post = 'Publicar' 30 | self.unlike = 'Descurtir' 31 | 32 | @staticmethod 33 | def digite_como_pessoa(frase, onde_digitar): 34 | for letra in frase: 35 | onde_digitar.send_keys(letra) 36 | time.sleep(random.randint(1, 8) / 30) 37 | 38 | @staticmethod 39 | def comentario_aleatorio(x, y, z): 40 | """ 41 | :param x: grupo de palavra inicial 42 | :param y: grupo de palavra secundaria 43 | :param z: emoji 44 | :return: uma frase sendo: (x + y) ou (x + z) ou (y + z) 45 | """ 46 | option = random.randrange(3) 47 | if option == 0: 48 | return random.choice(x) + random.choice(y) 49 | elif option == 1: 50 | return random.choice(y) + random.choice(z) 51 | else: 52 | return random.choice(x) + random.choice(z) 53 | 54 | def login(self): 55 | driver = self.driver 56 | driver.get("https://www.instagram.com/") 57 | time.sleep(5) 58 | user_name_elem = driver.find_element_by_xpath("//input[@name='username']") 59 | user_name_elem.clear() 60 | user_name_elem.send_keys(self.username) 61 | passworword_elem = driver.find_element_by_xpath("//input[@name='password']") 62 | passworword_elem.clear() 63 | passworword_elem.send_keys(self.password) 64 | passworword_elem.send_keys(Keys.RETURN) 65 | time.sleep(6) 66 | 67 | def selecionar_fotos_hashtags(self, hashtag): 68 | driver = self.driver 69 | driver.get("https://www.instagram.com/explore/tags/" + hashtag + "/") 70 | time.sleep(2) 71 | 72 | for _ in range(1, 7): 73 | try: 74 | driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") 75 | time.sleep(2) 76 | hrefs_na_tela = driver.find_elements_by_tag_name('a') 77 | hrefs_na_tela = [elem.get_attribute('href') for elem in hrefs_na_tela 78 | if '.com/p/' in elem.get_attribute('href')] 79 | [self.pic_hrefs.append(href) for href in hrefs_na_tela if href not in self.pic_hrefs] 80 | except Exception: 81 | continue 82 | 83 | def selecionar_fotos_local(self, link_local): 84 | driver = self.driver 85 | driver.get(link_local) 86 | time.sleep(2) 87 | 88 | for _ in range(1, 7): 89 | try: 90 | driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") 91 | time.sleep(2) 92 | hrefs_na_tela = driver.find_elements_by_tag_name('a') 93 | hrefs_na_tela = [elem.get_attribute('href') for elem in hrefs_na_tela 94 | if '.com/p/' in elem.get_attribute('href')] 95 | [self.pic_hrefs.append(href) for href in hrefs_na_tela if href not in self.pic_hrefs] 96 | except Exception: 97 | continue 98 | 99 | def listar_perfis_do_perfil(self, perfilrandom): 100 | driver = self.driver 101 | driver.get("https://www.instagram.com/" + perfilrandom + "/") 102 | time.sleep(2) 103 | self.driver.find_element_by_xpath("//a[@href= \"/" + perfilrandom + "/followers/\"" + "]").click() 104 | time.sleep(1) 105 | driver.find_element_by_class_name('PZuss').click() 106 | for _ in range(1, 10): 107 | try: 108 | driver.execute_script(''' 109 | var fDialog = document.querySelector('div[role="dialog"] .isgrP'); 110 | fDialog.scrollTop = fDialog.scrollHeight 111 | ''') 112 | time.sleep(4) 113 | hrefs_na_tela = driver.find_elements_by_tag_name('a') 114 | hrefs_na_tela = [elem.get_attribute('href') for elem in hrefs_na_tela 115 | if 'FPmhX notranslate _0imsa ' in elem.get_attribute('class')] 116 | [self.seguidores_perfil.append(href) for href in hrefs_na_tela if href not in self.seguidores_perfil] 117 | except Exception: 118 | continue 119 | 120 | def selecionar_fotos_perfil(self): 121 | driver = self.driver 122 | perfil_do_perfil = random.choice(self.seguidores_perfil) 123 | driver.get(perfil_do_perfil) 124 | time.sleep(2) 125 | limite_fotos = 0 126 | max_fotos = random.randrange(3, 7) 127 | 128 | for _ in range(1, 2): # Esse 'for' existe para facilitar novas funcionalidades 129 | try: 130 | driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") 131 | time.sleep(2) 132 | hrefs_na_tela = driver.find_elements_by_tag_name('a') 133 | hrefs_na_tela = [elem.get_attribute('href') for elem in hrefs_na_tela 134 | if '.com/p/' in elem.get_attribute('href')] 135 | self.numero_perfis_atual += 1 136 | for href in hrefs_na_tela: 137 | if href not in self.pic_hrefs and limite_fotos < max_fotos: 138 | self.pic_hrefs.append(href) 139 | limite_fotos += 1 140 | except Exception: 141 | continue 142 | self.seguidores_perfil.remove(perfil_do_perfil) 143 | 144 | def comentar_fotos(self, x, y, z, comentario=None): 145 | driver = self.driver 146 | for foto_atual in self.pic_hrefs: 147 | driver.get(foto_atual) 148 | time.sleep(5) 149 | try: 150 | comentario = driver.find_element_by_xpath(f"//a[contains(text(),'{self.username}')]") 151 | except: 152 | pass 153 | if comentario is None: 154 | try: 155 | time.sleep(random.randint(2, 4)) 156 | driver.find_element_by_class_name('Ypffh').click() 157 | campo_comentario = driver.find_element_by_class_name('Ypffh') 158 | time.sleep(random.randint(2, 4)) 159 | try: 160 | self.digite_como_pessoa(self.comentario_aleatorio(x, y, z), campo_comentario) 161 | except: 162 | pass 163 | time.sleep(random.randint(10, 20)) 164 | driver.find_element_by_xpath(f"//button[contains(text(),'{self.post}')]").click() 165 | self.numero_fotos += 1 166 | self.contador_stdout() 167 | time.sleep(random.randint(367, 603)) 168 | except Exception: 169 | time.sleep(5) 170 | time.sleep(5) 171 | comentario = None 172 | 173 | def like_foto(self, unlike=None): 174 | driver = self.driver 175 | for foto_atual in self.pic_hrefs: 176 | driver.get(foto_atual) 177 | try: 178 | time.sleep(4) 179 | unlike = self.driver.find_element_by_xpath("//*[@aria-label='{}']".format(self.unlike)) 180 | except Exception: 181 | pass 182 | if unlike is None: 183 | try: 184 | time.sleep(random.randint(6, 12)) 185 | self.driver.find_element_by_xpath("//*[@aria-label='{}']".format(self.like)).click() 186 | self.numero_fotos += 1 187 | self.contador_stdout() 188 | time.sleep(random.randint(45, 59)) 189 | except Exception: 190 | time.sleep(5) 191 | time.sleep(3) 192 | unlike = None 193 | 194 | def like_comentar(self, x, y, z, unlike=None): 195 | driver = self.driver 196 | 197 | for foto_atual in self.pic_hrefs: 198 | driver.get(foto_atual) 199 | try: 200 | time.sleep(4) 201 | unlike = self.driver.find_element_by_xpath("//*[@aria-label='{}']".format(self.unlike)) 202 | except Exception: 203 | pass 204 | if unlike is None: 205 | if self.contador_para_comentar % 10 == 0: 206 | try: 207 | time.sleep(random.randint(2, 10)) 208 | driver.find_element_by_class_name('Ypffh').click() 209 | campo_comentario = driver.find_element_by_class_name('Ypffh') 210 | time.sleep(random.randint(2, 4)) 211 | try: 212 | self.digite_como_pessoa(self.comentario_aleatorio(x, y, z), campo_comentario) 213 | except Exception: 214 | pass 215 | try: 216 | time.sleep(2) 217 | driver.find_element_by_xpath(f"//button[contains(text(),'{self.post}')]").click() 218 | time.sleep(random.randint(10, 20)) 219 | except: 220 | self.contador_para_comentar -= 1 221 | pass 222 | except Exception: 223 | time.sleep(2) 224 | try: 225 | time.sleep(random.randint(6, 12)) 226 | self.driver.find_element_by_xpath("//*[@aria-label='{}']".format(self.like)).click() 227 | self.numero_fotos += 1 228 | self.contador_stdout() 229 | time.sleep(random.randint(45, 59)) 230 | self.contador_para_comentar += 1 231 | except Exception: 232 | time.sleep(5) 233 | time.sleep(3) 234 | unlike = None 235 | 236 | def tabela_stdout(self): 237 | if self.stdout == 'com_hashtags(hashtags, tipo_busca, primeira_palavra, complemento, ' \ 238 | 'emoji, username, password, modo_bot, linguagem, navegador, inicio)': 239 | print('----------------------------------------') 240 | print(i18n.t('fotos_faltando_hash')) 241 | 242 | elif self.stdout == 'com_local(local, tipo_busca, primeira_palavra, complemento, ' \ 243 | 'emoji, username, password, modo_bot, linguagem, navegador, inicio)': 244 | print('----------------------------------------') 245 | print(i18n.t('fotos_faltando_hash')) 246 | 247 | elif self.stdout == 'com_perfil(perfis, tipo_busca, primeira_palavra, complemento, ' \ 248 | 'emoji, username, password, modo_bot, linguagem, navegador, inicio)': 249 | print('-------------------------------------------------------------------') 250 | print(i18n.t('seguidores_faltando')) 251 | 252 | def contador_stdout(self): 253 | for _ in range(150): 254 | if self.stdout == 'com_hashtags(hashtags, tipo_busca, primeira_palavra, complemento, ' \ 255 | 'emoji, username, password, modo_bot, linguagem, navegador, inicio)': 256 | sys.stdout.write(f"\r {len(self.pic_hrefs) - self.numero_fotos} |" 257 | f" {self.numero_fotos} |") 258 | sys.stdout.flush() 259 | time.sleep(0.5) 260 | elif self.stdout == 'com_local(local, tipo_busca, primeira_palavra, complemento, ' \ 261 | 'emoji, username, password, modo_bot, linguagem, navegador, inicio)': 262 | sys.stdout.write(f"\r {len(self.pic_hrefs) - self.numero_fotos} |" 263 | f" {self.numero_fotos} |") 264 | sys.stdout.flush() 265 | time.sleep(0.5) 266 | 267 | elif self.stdout == 'com_perfil(perfis, tipo_busca, primeira_palavra, complemento, ' \ 268 | 'emoji, username, password, modo_bot, linguagem, navegador, inicio)': 269 | sys.stdout.write( 270 | f"\r {len(self.seguidores_perfil)} |" 271 | f" {self.numero_perfis_atual} |" 272 | f" {self.numero_fotos} | ") 273 | sys.stdout.flush() 274 | time.sleep(0.5) 275 | 276 | def limpar_urefs(self): 277 | self.pic_hrefs.clear() 278 | 279 | def deu_ruim(self): 280 | self.driver.close() 281 | print(i18n.t('deu_ruim')) 282 | sys.exit() 283 | 284 | def close_browser(self): 285 | self.driver.close() 286 | --------------------------------------------------------------------------------