2

I am trying to solve my problems making searches, reading documentations.

The problem

I want to get all youtube titles from an youtube channel using python beautiful soup. Youtube loads dynamically, i think with JavaScript, without I just can not get any title, So i used the I was able to get titles from youtube channel. The problem is that i need to load all the videos. I can just load the 29 ou 30 first ones. I am thinking on simulating a scroll down or somthing like that. I can not find how to do this on beatiful soup without selenium...

Version and libraries used:

I am trying not using selenium or scrapy

  • I want to understand what i can and what i can not do with BeautifulSoup on dynamic pages.

The code

from bs4 import BeautifulSoup
import sys
import urllib.request
from PyQt5.QtWebEngineWidgets import QWebEnginePage
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import QUrl


class Page(QWebEnginePage):
    def __init__(self, url):
        self.app = QApplication(sys.argv)
        QWebEnginePage.__init__(self)
        self.html = ''
        self.loadFinished.connect(self._on_load_finished)
        self.load(QUrl(url))
        self.app.exec_()

    def _on_load_finished(self):
        self.html = self.toHtml(self.Callable)
        print('Load finished')

    def Callable(self, html_str):
        self.html = html_str
        self.app.quit()


def main():
    page = Page(
        'https://www.youtube.com/channel/UCY50YjnaeI7ezvRagFWeWmw/videos')
    soup = BeautifulSoup(page.html, 'html.parser')
    what_i_search = soup.find('div', class_='style-scope ytd-grid-renderer')
    youtube_titles = what_i_search.find_all('h3')
    line = 0
    for youtube_title in youtube_titles:
        line = line + 1
        print(str(line) + ". " + youtube_title.text)


if __name__ == '__main__':
    main()

Output:

Load finished
1. Plants vs Zombies 2 - FUTURO DISTANTE - dias 09, 10, 11 e 12 - Parte 48
2. Marisa ou M�nica - Turma da M�nica
3. Jogo PET DOLL - Parte 02
4. A Magali n�o mora mais aqui - Turma da M�nica
5. MOY'S WORLD (parte 03) - Mundo 01 - N�vel 6 - Gameplay
6. M�nica e Cebolinha em "uma pedra no meio do caminho" - Turma da M�nica
7. Plants vs Zombies 2 - FUTURO DISTANTE - dia 08 e Terror do Amanh� - Parte 47
8. Magali em "N�o Sou Boneca" - Turma da M�nica
9. Talking Tom HERO DASH - Terminamos todas os mundos!!! - Parte 13
10. M�nica em Maternidade Saud�vel - Turma da M�nica
11. Rotina Matinal da Beb� Reborn da Luisa - Cuidando da boneca
12. Bloop Go! Jogo/Game - Parte 1
13. Chico Bento em "se n�o fosse a fei�ra" - Turma da M�nica
14. Luisa cuidando do gatinho BUBBU - parte 3
15. Turma da M�nica em "as tr�s letrinhas" - Feliz dia das m�es!!!
16. Plants vs Zombies 2 - FUTURO DISTANTE - dias 05, 06 e 07 - Parte 46
17. A Turma em "fregu�s tem sempre raz�o" - Turma da M�nica
18. Dollify - parte 4 - Brincando de fazer caricaturas! Fazendo o boneco da prima Julia
19. Casc�o em "o maior bode" - Turma da M�nica
20. Jogo Hotel Transilv�nia Adventures - parte 12 - Terminamos a COZINHA - Fases 55 e 56
21. Turma do Penadinho em "sexta-feira 14" - Turma da M�nica
22. Plants vs Zombies 2 - iniciamos FUTURO DISTANTE - dias 01, 02, 03 e 04 - Parte 45
23. Chico Bento e o Nome - Turma da M�nica
24. Talking Angela - Jogando com a Gatinha Angela - N�vel 20 (parte 9)
25. Magali e a Turma em "OS ADOLESCENTES" - Turma da M�nica
26. Cortando o cabelo (franjinha) da Luisa em casa! #m�eefilha
27. Jogo Gardenscapes - Parte 11 - Cuidando do Jardim e do Tot� - N�vel 48
28. Cebolinha em "a caveira dentu�a" - Turma da M�nica
29. Plants vs Zombies 2 - TERMINAMOS A CIDADE PERDIDA - dias 30, 31 e 32 - Parte 44
30. Magali em Comida Falante - Turma da M�nica

Andre Nevares
  • 711
  • 6
  • 21
  • 1
    you can use Youtube Data api /search : https://developers.google.com/youtube/v3/docs/search/list to get all video titles by channel id – Bertrand Martel May 24 '20 at 19:38
  • Bernard... Thanks for such an nice feedback... In this case, Youtube has an API... But other dinamic pages do not have it... I am wondering if i want to get all data from a dynamic page with no API, without Selenium... THanks for your kind feedback ok? – Andre Nevares May 24 '20 at 23:04

1 Answers1

2

As you point out you have to scroll down until the "spinner" does not appear but for this you need to use a QWebEngineView, and then you can get the text as you have done or use QWebChanel as I implement it in my answer. So that the QWebEngineView is not shown I have activated the attribute Qt::WA_DontShowOnScreen.

import sys
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets, QtWebChannel

SCRIPT = """
var timeout = 1 * 1000;

var backend = null;

new QWebChannel(qt.webChannelTransport, function (channel) {
    backend = channel.objects.backend;
});

function try_send_data(){
    if(document.getElementById("spinner")){
        document.documentElement.scrollTo(0, document.body.scrollHeight || document.documentElement.scrollHeight);
        setTimeout(try_send_data, timeout)
        return
    }
    else{
        var titles = []
        for (let item of document.getElementsByClassName("yt-simple-endpoint style-scope ytd-grid-video-renderer")) {
            titles.push(item.innerText);
        }
        backend.set_titles(titles);
    }
}
setTimeout(try_send_data, timeout);
"""


class YTScrapper(QtCore.QObject):
    def __init__(self, url, parent=None):
        super().__init__(parent)

        self._titles = []

        app = QtWidgets.QApplication(sys.argv)
        self._page = QtWebEngineWidgets.QWebEnginePage(self)

        channel = QtWebChannel.QWebChannel(self)
        self.page.setWebChannel(channel)

        self._view = QtWebEngineWidgets.QWebEngineView()
        self._view.setAttribute(QtCore.Qt.WA_DontShowOnScreen, True)
        self._view.setPage(self.page)
        self._view.resize(1920, 1080)
        self._view.show()

        self.page.loadFinished.connect(self.on_load_finished)

        self.page.load(QtCore.QUrl(url))
        app.exec_()

    @property
    def titles(self):
        return self._titles

    @property
    def page(self):
        return self._page

    @QtCore.pyqtSlot(bool)
    def on_load_finished(self, ok):
        if ok:
            file = QtCore.QFile(":/qtwebchannel/qwebchannel.js")
            if file.open(QtCore.QIODevice.ReadOnly):
                content = file.readAll()
                file.close()
                self.page.runJavaScript(content.data().decode())
            self.page.webChannel().registerObject("backend", self)

            self.page.runJavaScript(SCRIPT)
        else:
            QtCore.QCoreApplication.quit()

    @QtCore.pyqtSlot(list)
    def set_titles(self, titles):
        self._titles = titles
        QtCore.QCoreApplication.quit()


def main():
    yt_scrapper = YTScrapper(
        "https://www.youtube.com/channel/UCY50YjnaeI7ezvRagFWeWmw/videos"
    )
    for i, title in enumerate(yt_scrapper.titles, start=1):
        print(f"{i}. {title.encode('utf-8')}")


if __name__ == "__main__":
    main()

Output:

1. Plants vs Zombies 2 - FUTURO DISTANTE - dias 09, 10, 11 e 12 - Parte 48
2. Marisa ou Mônica - Turma da Mônica
3. Jogo PET DOLL - Parte 02
4. A Magali não mora mais aqui - Turma da Mônica
5. MOY'S WORLD (parte 03) - Mundo 01 - Nível 6 - Gameplay
6. Mônica e Cebolinha em "uma pedra no meio do caminho" - Turma da Mônica
7. Plants vs Zombies 2 - FUTURO DISTANTE - dia 08 e Terror do Amanhã - Parte 47
8. Magali em "Não Sou Boneca" - Turma da Mônica
9. Talking Tom HERO DASH - Terminamos todas os mundos!!! - Parte 13
10. Mônica em Maternidade Saudável - Turma da Mônica
11. Rotina Matinal da Bebê Reborn da Luisa - Cuidando da boneca
12. Bloop Go! Jogo/Game - Parte 1
13. Chico Bento em "se não fosse a feiúra" - Turma da Mônica
14. Luisa cuidando do gatinho BUBBU - parte 3
15. Turma da Mônica em "as três letrinhas" - Feliz dia das mães!!!
16. Plants vs Zombies 2 - FUTURO DISTANTE - dias 05, 06 e 07 - Parte 46
17. A Turma em "freguês tem sempre razão" - Turma da Mônica
18. Dollify - parte 4 - Brincando de fazer caricaturas! Fazendo o boneco da prima Julia
19. Cascão em "o maior bode" - Turma da Mônica
20. Jogo Hotel Transilvânia Adventures - parte 12 - Terminamos a COZINHA - Fases 55 e 56
21. Turma do Penadinho em "sexta-feira 14" - Turma da Mônica
22. Plants vs Zombies 2 - iniciamos FUTURO DISTANTE - dias 01, 02, 03 e 04 - Parte 45
23. Chico Bento e o Nome - Turma da Mônica
24. Talking Angela - Jogando com a Gatinha Angela - Nìvel 20 (parte 9)
25. Magali e a Turma em "OS ADOLESCENTES" - Turma da Mônica
26. Cortando o cabelo (franjinha) da Luisa em casa! #mãeefilha
27. Jogo Gardenscapes - Parte 11 - Cuidando do Jardim e do Totó - Nível 48
28. Cebolinha em "a caveira dentuça" - Turma da Mônica
29. Plants vs Zombies 2 - TERMINAMOS A CIDADE PERDIDA - dias 30, 31 e 32 - Parte 44
30. Magali em Comida Falante - Turma da Mônica
31. Meu Tom 2 (jogo/gameplay) - Parte 11 - O Tom está crescendo novamente: 10 anos!
32. Desencaixotando a Magali - Turma da Mônica
33. Plants vs Zombies 2 - CIDADE PERDIDA - dias 26, 27, 28 e 29 - Parte 43
34. Mônica em "tem alguma coisa escrita no tronco da árvore" - Turma da Mônica
35. CHAVES Adventures (Jogo/Game) - Nível 04
36. Luca e Cebolinha em "devolve minha cadeira" - Turma da Mônica
37. MOY'S WORLD (parte 02) - Mundo 01 - Níveis 4 e 5 - Gameplay
38. Historinha da Magali - Turma da Mônica
39. Quebra-cabeça da Masha e o Urso - Diversão em família!
40. Super Phantom Cat 2 - ÁRVORE ANCIÃ - Nível 3 - parte 13 (Gameplay/Jogo)
41. Mônica em "Pixador Apaixonado" - Turma da Mônica
42. Dentista: Cuidando dos animais (Jogo/Game)
43. PÁSCOA - Turma da Mônica
44. Plants vs Zombies 2 - CIDADE PERDIDA - dias 21, 22, 23, 24 e 25 - Parte 42
45. Chico Bento em 1° de Abril
46. Talking Tom HERO DASH - LIGUE O TURBO! - Parte 12
47. Turma da Mônica em "sobre luzes e balões"
48. Meu Tom 2 (jogo/gameplay) - Parte 10 - O Tom voltou a ter 6 anos
49. Mônica em "correndo atrás do Luca" - Turma da Mônica
....
....
600. Cebolinha tentando fugir da Mônica - Turma da Mônica
601. Mônica brincando de casinha com Cascão e Cebolinha - Turma da Mônica
602. Mônica e Cebolinha na praia - Turma da Mônica
603. Chico Bento sem sono - Turma da Mônica
604. Bidu em uma aventura mal assombrada - Turma da Mônica
605. O aniversário da Magali - Turma da Mônica
606. Cebolinha e o lobisomem - Turma da Mônica
607. Parabéns pra você - Galinha Pintadinha, Pintinho Amarelinho, aniversário do Ursinho, nosso clipe
Andre Nevares
  • 711
  • 6
  • 21
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • I had copy and this code ... But output was Warning: QT_DEVICE_PIXEL_RATIO is deprecated. Instead use: QT_AUTO_SCREEN_SCALE_FACTOR to enable platform plugin controlled per-screen factors. QT_SCREEN_SCALE_FACTORS to set per-screen DPI. QT_SCALE_FACTOR to set the application global scale factor. [6220:19660:0525/003047.675:ERROR:cache_util_win.cc(21)] Unable to move the cache: Acesso negado. (0x5) [6220:19660:0525/003047.675:ERROR:cache_util.cc(141)] Unable to move cache folder C:\Users\MICRO\AppData\Local\py (.....) – Andre Nevares May 25 '20 at 03:33
  • @AndreNevares The part you show are common warnings that do not indicate the error, it could be the other part of the error message that gives more information about the problem, you can edit your question and add the entire error message. – eyllanesc May 25 '20 at 04:02
  • The error has more character then allowed i will post hole on git... https://github.com/andrenevares/andrenevares/blob/master/cmd/error.md – Andre Nevares May 25 '20 at 12:48
  • 1
    @AndreNevares try changing to `print(f"{i}. {title.encode("utf-8")}")`. The error has nothing to do with my solution but with the encoding of the text, check https://stackoverflow.com/questions/27092833/unicodeencodeerror-charmap-codec-cant-encode-characters – eyllanesc May 25 '20 at 13:37
  • OH YEAH!!!!!!!!! It worked JUST FINE!!!! I just have to change the double quote of "utf-8" for single quotes 'utf-8' cause the print was using double quotes... Aweasome!!! Thank you so very much! What´s the better practice in stackoverflow? Is it better to put (SOLVED)? Edit the main post? What do you suggest? Also... Do you have any books or suggestion to improve myself? Thank you so very much!!!!!! – Andre Nevares May 25 '20 at 21:40
  • @AndreNevares No, none of these practices are valid in SO. In SO to indicate that a solution has been found, mark an answer as correct, for this you must press the tick on the left side of the answer. For more information, read the [tour] as it is explicitly indicated there. – eyllanesc May 25 '20 at 21:45
  • thanks for such an incredible feedback! Long life to Stack Overflow! – Andre Nevares May 25 '20 at 22:21