1
votes

Grattage Web Python avec beautifulsoup - impossible d'extraire le chercheur principal de Clinicaltrials.gov

(Clause de non-responsabilité: je suis un noob Python et web scraping, mais je fais de mon mieux pour apprendre).

J'essaie d'extraire 3 points de données clés d'études de recherche sur Clinicaltrials.gov. Ils ont une API, mais l'API ne capture pas les éléments dont j'ai besoin. Je veux obtenir une (1) brève description de l'étude, (2) le chercheur principal (IP) et (3) quelques mots-clés associés à l'étude. Je crois que mon code capture 1 et 3, mais pas 2. Je n'arrive pas à comprendre pourquoi je n'obtiens pas le nom du (des) chercheur (s) principal (s). Voici les deux sites que j'ai dans mon code:

https: // Clinicaltrials. gov / ct2 / show / NCT03530579 https://clinicaltrials.gov/ct2/show/NCT03436992

Voici mon code (je sais que le code PI est erroné, mais je voulais démontrer que j'ai essayé):

import pandas as pd
import requests
from bs4 import BeautifulSoup
import csv   

fields=['PI','Project_Summary', 'Keywords']
with open(r'test.csv', 'a') as f:
     writer = csv.writer(f)
     writer.writerow(fields)

urls = ['https://clinicaltrials.gov/ct2/show/NCT03436992','https://clinicaltrials.gov/ct2/show/NCT03530579']
for url in urls:

     response = requests.get(url)
     soup = BeautifulSoup(response.content, 'html.parser')
     #get_keywords
     for rows in soup.find_all("td"):
          k = rows.get_text()     
          Keywords = k.strip()
     #get Principal Investigator   
     PI = soup.find_all('padding:1ex 1em 0px 0px;white-space:nowrap;')

     #Get description    
     Description = soup.find(class_='ct-body3 tr-indent2').get_text()
     d = {'Summary2':[PI,Description,Keywords]} 

     df = pd.DataFrame(d)
     print (df)
     import csv   
     fields=[PI,Description, Keywords]
     with open(r'test.csv', 'a') as f:
          writer = csv.writer(f)
          writer.writerow(fields)


1 commentaires

J'ai réussi à obtenir le PI, mais je ne vois pas non plus de mots-clés capturés. Quels sont les mots clés que vous essayez d'obtenir.


3 Réponses :


3
votes

Vous pourrez peut-être utiliser le sélecteur suivant

i.e. PI = soup.select_one ('. tr-table_cover [headers = name]'). text

import requests
from bs4 import BeautifulSoup  
urls = ['https://clinicaltrials.gov/ct2/show/NCT03530579', 'https://clinicaltrials.gov/ct2/show/NCT03436992','https://clinicaltrials.gov/show/NCT03834376']
with requests.Session() as s:
    for url in urls:
        r = s.get(url)
        soup = BeautifulSoup(r.text, "lxml")
        item = soup.select_one('.tr-table_cover [headers=name]').text if soup.select_one('.tr-table_cover [headers=name]') is not None else 'No PI'
        print(item)

Le . code > est un sélecteur de classe et le [] est un sélecteur attribut . L'espace entre est un combinateur descendant a> précisant que l'élément récupéré à droite est un enfant de celui de gauche


3 commentaires

Merci à tous! J'ai utilisé le code de QHarr et pour la plupart, cela a fonctionné. Cependant, lorsque j'ajoute une URL clinictrials.gov et qu'aucun PI n'est répertorié, j'obtiens une erreur "L'objet" NoneType "n'a pas d'attribut" texte ". Comment pourrais-je contourner cela?


Je vais éditer pour vous. Pouvez-vous fournir l'URL défaillante actuelle pour que je puisse ajouter au code


Merci! J'apprécie vraiment cela.



2
votes

J'ai simplement utilisé des pandas pour obtenir les tables. cela renverra une liste de dataframes. Vous pouvez ensuite les parcourir pour rechercher le PI:

tables = pd.read_html(url)
for table in tables:
    try:
        if 'Principal Investigator' in table.iloc[0,0]:
            pi =  table.iloc[0,1]
    except:
        continue


0 commentaires

0
votes

Il y a donc de nombreuses façons de descendre dans l'arborescence DOM et votre chemin est très "cassant". Cela signifie que les sélecteurs que vous avez choisis pour commencer votre recherche sont extrêmement spécifiques et liés au style CSS qui peut changer beaucoup plus facilement que la structure du document dans son ensemble.

Mais si j'étais vous, je filtrerais certains nœuds sur certains critères, puis concentrez-vous sur ce groupe spécifique pendant que vous passez au crible le bruit.

Donc, en regardant ces URL que vous avez montrées, il semble que les données sont bien structurées et utilisent des tableaux. Sur cette base, nous pouvons faire des hypothèses telles que

  1. Ce sont des données qui se trouvent dans une table
  2. Il contiendra la chaîne "investigateur principal" à l'intérieur
principal_investigator = [ele for ele in refined_tables.findAll('td') if 'name' in ele.attrs['headers']][0].text

À ce stade, nous avons un candidat fort dans notre liste de tables_finies qui pourrait en fait contenir notre table primaire et est idéalement de taille 1 en supposant que " Le filtre du chercheur principal "que nous avons utilisé ne se trouve nulle part ailleurs dans d'autres tables.

# get all the tables in the page
tables = soup.find_all('table')
# now filter down to a smaller set of tables that might contain the info
refined_tables = [table for table in tables if 'principal investigator' in str(table).lower()]

Ici, ce qui a été fait en regardant le site, c'est qu'ils utilisent l'attribut headers pour attribuer le rôle de la balise td dans la ligne du tableau.

Donc, en substance, pensez-y à partir d'un niveau supérieur et commencez à réduire les choses comme autant que possible en étapes simples pour vous aider à trouver ce que vous cherchez.


0 commentaires