Гузариш ба мундариҷа
Мавзӯи 4 аз 5

Гардиш дар дарахт ва ҷадвалҳо

Чаро дар дарахт мегардем

Баъзан ёфтани порчаи намоён ва баъд расидан ба блоки бо он алоқаманд осонтар аст. Масалан, нишона дар дохили корт ва тавзеҳ дар назди он аст. Ҷустуҷӯи умумии ҳамаи тавзеҳҳо намегӯяд, ки кадомаш ба кадом корт тааллуқ дорад. Гардиш дар дарахт ин робитаро нигоҳ медорад.

Гиреҳ унсури дарахт аст: он метавонад тег ё матн бошад. Волидайн гиреҳро бевосита дар бар мегирад, фарзандон бевосита дар дохили он ҳастанд ва ҳамсояҳо як волидайн доранд. Наслҳо фарзандон, фарзандони онҳо ва ғайраро дар бар мегиранд. Ин муносибатҳо барои таҳлили кортҳо, рӯйхати таърифҳо ва ҷадвалҳо бе вобастагӣ ба ороиш ёрӣ медиҳанд.

1. Ба боло: волидайн ва ниёгон

parent як қадам ба боло аст. parents ҳамаи ниёгонро аз наздиктарин то берунтарин медиҳад. Ҳар тег name дорад. Объекти решавии BeautifulSoup низ дар ин занҷир ҳаст; номаш [document] аст. find_parent("section") ниёгони наздиктарини мувофиқро меҷӯяд, на танҳо як қадам боло меравад.

from bs4 import BeautifulSoup

soup = BeautifulSoup('<section id="box"><div><span>A</span></div></section>', "html.parser")
span = soup.span
print(span.parent.name)
print([tag.name for tag in span.parents])
print(span.find_parent("section")["id"])

Натиҷа:

div
['div', 'section', '[document]']
box

2. Ба поён: фарзандон ва наслҳо

children фарзандони бевосита, аз ҷумла гиреҳҳои матниро медиҳад. Гузариши сатр байни тегҳо ҳам матн ва ҳам фарзанд аст. name-и гиреҳи матнӣ ба None баробар аст. Агар танҳо тегҳои сатҳи якум лозим бошанд, find_all(recursive=False)-ро истифода баред. Ҷустуҷӯи одӣ ба ҳар чуқурӣ меравад.

from bs4 import BeautifulSoup

soup = BeautifulSoup("<div>\n<p>A <b>B</b></p>\n</div>", "html.parser")
print([child.name for child in soup.div.children])
print([tag.name for tag in soup.div.find_all(recursive=False)])
print([tag.name for tag in soup.div.find_all()])

Натиҷа:

[None, 'p', None]
['p']
['p', 'b']

descendants ҳамаи гиреҳҳои дохилӣ, аз ҷумла матнро медиҳад. children, descendants ва parents итератор мебошанд, на рӯйхати тайёр. Барои нигоҳ доштани натиҷа онҳоро ба list табдил диҳед. Итератори тамомшуда аз нав оғоз намешавад; муроҷиати нави хосият гардиши нав месозад.

from bs4 import BeautifulSoup

div = BeautifulSoup("<div><p>A<b>B</b></p></div>", "html.parser").div
print([node.name for node in div.descendants])
items = div.children
print(len(list(items)))
print(len(list(items)))
print(len(list(div.children)))

Натиҷа:

['p', None, 'b', None]
1
0
1

3. Ба паҳлу: ҳамсояҳо

next_sibling ва previous_sibling гиреҳи ҳамсояи бевоситаро медиҳанд; он метавонад фосила ё гузариши сатр бошад. Методҳои find_next_sibling ва find_previous_sibling матнро гузаронда, тегҳои ҳамсояро меҷӯянд. Номи тегро ҳам додан мумкин аст. Шакли ҷамъ, масалан find_next_siblings, ҳамаи тегҳои мувофиқи баъдиро медиҳад.

from bs4 import BeautifulSoup

html = "<section><h4>A</h4>\n<p>B</p><p>C</p></section>"
soup = BeautifulSoup(html, "html.parser")
heading = soup.h4
print(repr(heading.next_sibling))
print(heading.find_next_sibling("p").get_text())
print([tag.get_text() for tag in heading.find_next_siblings("p")])
print(soup.p.find_previous_sibling().name)

Натиҷа:

'\n'
B
['B', 'C']
h4

Ҷустуҷӯи ҳамсояҳо дар ҳудуди як волидайн мемонад. Он ба дохили блоки ҳамсоя намедарояд. Инро аз ҷустуҷӯи унсури навбатӣ дар ҳар ҷойи ҳуҷҷат фарқ кунед. Агар маълумоти алоқаманд дар чуқуриҳои гуногун бошад, аввал контейнери умумиро гирифта, баъд дар шохаи он ҷустуҷӯ кунед.

from bs4 import BeautifulSoup

soup = BeautifulSoup("<div><b>A</b><section><p>B</p></section><p>C</p></div>", "html.parser")
print(soup.b.find_next_sibling("p").get_text())
print(soup.section.find("p").get_text())

Натиҷа:

C
B

4. Сохтори ҷадвал

table ҷадвал, tr сатр, th катаки сарлавҳа ва td катаки одӣ аст. Гурӯҳҳои thead, tbody, tfoot метавонанд сатҳи иловагии дохилшавӣ созанд. Пас фарзандони бевоситаи ҷадвал ҳатман сатр нестанд. Барои ҷадвали одии бе ҷадвали дохилӣ find_all("tr") сатрҳоро аз дохили ин пардаҳо меёбад. Катакҳои якҷояшудаи rowspan ва colspan мантиқи ҷудогона мехоҳанд; дар ин ҷо онҳо нестанд.

from bs4 import BeautifulSoup

html = "<table><thead><tr><th>A</th><th>B</th></tr></thead><tbody><tr><td>4</td><td><b>6</b></td></tr></tbody></table>"
soup = BeautifulSoup(html, "html.parser")
rows = soup.table.find_all("tr")
print(len(rows))
print([cell.get_text() for cell in rows[0].find_all(["th", "td"])])
print(len(rows[1].find_all("td", recursive=False)))

Натиҷа:

2
['A', 'B']
2

Дар дохил чӣ рӯй медиҳад

Гиреҳҳои дарахт бо волидайн ва ҳамсояҳо робита доранд. Гардиш ба ҷойи таҳлили нави сатри аслӣ ҳамин робитаҳоро истифода мебарад. Дарахти насабро тасаввур кунед: ҷустуҷӯи волидайн боло, ҷустуҷӯи фарзандон поён ва ҷустуҷӯи ҳамсояҳо дар як сатҳ меравад. Тартиби гардишро ҷойи гиреҳҳо дар ҳуҷҷат муайян мекунад.

Барои ҷадвал аввал шумораи сатрҳо ва таркиби сатри якумро санҷида, баъд муайян кунед, ки кадом сатрҳо маълумотанд. find_all(["th", "td"]) тартиби омехтаи катакҳоро нигоҳ медорад. Агар аввал ҳамаи th ва баъд ҳамаи td-ро ҷамъ кунед, тартиб метавонад дигар шавад. Бе санҷиш ҷадвали мураккабро росткунҷа ҳисоб накунед.

Хатоҳои маъмул

1. Танҳо тегҳоро фарзанд ҳисобидан. <div>\n<p>A</p>\n</div> се гиреҳи фарзанд, вале як тег дорад. Мисли мисоли боло find_all(recursive=False)-ро истифода баред.

2. Аз .next_sibling тег интизор шудан. Дар боло \n ҳосил шуд. Барои теги ҳамсояи баъдӣ find_next_sibling()-ро гиред.

3. Омехтани find_all ва фарзандони бевосита. Ҷустуҷӯ пешфарз рекурсивӣ аст: рӯйхати дохилӣ низ ба интихоб медарояд. Агар маънои масъала ба як сатҳ дахл дошта бошад, чуқуриро маҳдуд кунед.

from bs4 import BeautifulSoup

ul = BeautifulSoup("<ul><li>A<ul><li>B</li></ul></li></ul>", "html.parser").ul
print(len(ul.find_all("li")))
print(len(ul.find_all("li", recursive=False)))

Натиҷа:

2
1

4. [document]-ро теги HTML ҳисобидан. Он дар занҷири ниёгон ҳаст, вале решаи хизматии BeautifulSoup мебошад. Агар занҷири тегҳои воқеӣ лозим бошад, онро истисно кунед. Ҳамчунин parents аз ниёгони наздик ба берун меравад, на аз реша ба унсур.

from bs4 import BeautifulSoup

span = BeautifulSoup("<main><span>A</span></main>", "html.parser").span
print([tag.name for tag in span.parents])
print([tag.name for tag in span.parents if tag.name != "[document]"])

Натиҷа:

['main', '[document]']
['main']

Ёддошт

  • parent, parents, find_parent(...) — як қадам боло, ҳамаи ниёгон, ҷустуҷӯи ниёгон.
  • children — фарзандони бевосита бо матн.
  • find_all(recursive=False) — танҳо тегҳои фарзанди бевосита.
  • descendants — ҳамаи гиреҳҳои дохилӣ; find_all() — ҳамаи тегҳои дохилӣ.
  • next_sibling — гиреҳи ҳамсоя; find_next_sibling(...) — ҷустуҷӯи теги ҳамсоя.
  • find_next_siblings(...), find_previous_sibling(...) — ҳамаи баъдӣ ва наздиктарини пешина.
  • table → tr → th/td — сатҳҳои ҷадвал; пардаҳо чуқурӣ илова мекунанд.