├── 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 | 
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 | 
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 | 
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 |
--------------------------------------------------------------------------------