Каков наилучший способ циклически перебирать массив элементов из HTML, чтобы использовать 2 отдельных имени тегов в порядке их появления?

#python #json #selenium

#python #json #selenium

Вопрос:

Не совсем уверен, как правильно сформулировать этот вопрос, но я в основном играю с python и использую Selenium для очистки веб-сайта, и я пытаюсь создать файл JSON с данными.

Вот цель, которую я стремлюсь достичь:

 {
 "main1" : {
    "sub1" : "data",
    "sub2" : "data",
    "sub3" : "data",
    "sub4" : "data"
  },
  "main2" : {
    "sub1" : "data",
    "sub2" : "data",
    "sub3" : "data",
    "sub4" : "data"
  }
}
 

Проблема, с которой я сталкиваюсь в данный момент, заключается в том, что на веб-сайте нет отступов или дочерних элементов. Это выглядит так (но, конечно, более длинная и фактическая копия):

 <h3>Main1</h3>
<p>Sub1</p>
<p>Sub2</p>
<p>Sub3</p>
<p>Sub4</p>
<h3>Main2</h3>
 

Теперь я хочу выполнить итерацию по HTML, чтобы использовать <h3> теги в качестве родительских («Main» в примере JSON) и <p> теги в качестве дочерних (sub[num]). Я новичок как в python, так и в Selenium, поэтому, возможно, я сделал это неправильно, но я пытался использовать items.find_elements_by_tag_name('el') для разделения двух, но я не знаю, как собрать их обратно в том порядке, в котором они были изначально.

Затем я попробовал перебирать все элементы и разделять теги if (item.tag_name == "el"): с помощью циклов. Это отлично работает, когда я печатаю результаты каждого цикла, но когда дело доходит до их объединения в JSON-файл, у меня возникает та же проблема, что и в предыдущем методе, когда я, похоже, не могу объединить 2. Я попробовал несколько вариантов, и я либо получаю ключевые ошибки, либо записывается только последний элемент в цикле.

Просто для справки, вот код для этого шага:

 items = browser.find_element_by_xpath(
    '//*[@id="main-content"]') #Main Content

itemList = items.find_elements_by_xpath(".//*")
statuses = [
    "Status1",
    "Status2",
    "Status3",
    "Status4"
]

for item in itemList: #iterate through the HTML
    if (item.tag_name == "h3"): #Separate H3 Tags
        main = item.text
        print("======================================")
        print(main)
        print("======================================")

    if (item.tag_name == 'p'): #Separate P tags
        for status in statuses: 
            if(status in item.text): #Filter P tags to only display info that contains words in the Status array
                delimeters = ":", "(", "See"
                regexPattern = "|".join(map(re.escape, delimeters))
                zoneData = re.split(regexPattern, item.text)
                
                #Split P tags into separate parts
                sub1 = zoneData[0] 
                sub2 = zoneData[1].translate({ord('*'): None})
                sub3 = zoneData[2].translate({ord(")"): None})

                print(sub1)
                print(sub2)
                print(sub3)

 

Последний вариант, который я решил попробовать, — это попробовать снова просмотреть весь HTML, но используя enumerate() и используя идентификаторы элемента и включая все теги между 2 идентификаторами, но я пока не совсем уверен, каков мой план действий с этим.

В общем, последний вариант кажется немного запутанным, и я почти уверен, что есть более простой способ сделать это. Что бы вы предложили?

Ответ №1:

Вот моя идея, но я не делал часть данных, вы можете добавить ее позже.

Я предполагаю, что в главном имени нет дубликатов, иначе вы потеряете некоторую информацию.

 
items = browser.find_element_by_xpath(
    '//*[@id="main-content"]') #Main Content
itemList = items.find_elements_by_xpath(".//p|.//h3") # only finds h3 or p

def construct(item_list):
    current_main = ''
    final_dict: dict = {}
    for item in item_list:
        if item.tag_name == "h3":
            current_main = item.text
            final_dict[current_main] = {} # create empty dict inside main. remove if you want to update the main dict
        if item.tag_name == "p":
            p_name = item.text
            final_dict[current_main][p_name] = "data"
    return final_dict