artaquest commited on
Commit
3d84c6c
·
verified ·
1 Parent(s): 1ccac36

wikiharvest bundle for the Azure containers (public inputs: the undated-marriage pool + sitelinks)

Browse files
wikiharvest/found.csv.gz ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9ceffb7310338057cfe71a4ae1e2c98d2c485d81cdef906532a801f457a38d64
3
+ size 20
wikiharvest/wiki_start_harvest.py CHANGED
@@ -139,8 +139,55 @@ def sitelinks():
139
 
140
 
141
  # ── phase C: the wikitext harvest ────────────────────────────────────────────────────────────────────────────────
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  def _find_dates(lang, text, spouse_names):
143
- """(start_iso, prec, snippet) for the marriage to this spouse in this wikitext, or None. prec 11 day / 9 year."""
144
  esc = [re.escape(n) for n in spouse_names if n and len(n) > 2]
145
  if not esc:
146
  return None
@@ -151,32 +198,63 @@ def _find_dates(lang, text, spouse_names):
151
  body = m.group(1)
152
  if not re.search(name_re, body, re.I):
153
  continue
154
- iso = re.search(YEAR + r"-(\d\d)-(\d\d)", body)
155
- if iso:
156
- return iso.group(0), 11, body[:160]
157
- MONTHS_EN = {m: i + 1 for i, m in enumerate(("january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december"))}
158
- d1 = re.search(r"(\d{1,2})\s+([A-Za-z]+)\s+" + YEAR, body) or re.search(r"([A-Za-z]+)\s+(\d{1,2}),\s*" + YEAR, body)
159
- if d1:
160
- g = d1.groups(); mon = MONTHS_EN.get((g[1] if g[0].isdigit() else g[0]).lower()); day = int(g[0] if g[0].isdigit() else g[1]); yr4 = g[2]
161
- if mon:
162
- return f"{yr4}-{mon:02d}-{day:02d}", 11, body[:160]
163
  yr = re.search(YEAR, body)
164
  if yr:
165
- return yr.group(0) + "-00-00", 9, body[:160]
166
  # 2. the spouse parameter's value line: |spouse = [[Name]] (m. 1903; div 1919) / ⚭ 1903 / с 1903
167
  for par in SPOUSE_PARAMS.get(lang, []) + SPOUSE_PARAMS.get("en", []):
168
  for m in re.finditer(r"\|\s*" + re.escape(par) + r"\s*=([^\n]*?" + name_re + r"[^\n]*)", text, re.I):
169
  line = m.group(1)
170
- yr = re.search(YEAR, line)
171
- if yr:
172
- return yr.group(0) + "-00-00", 9, line[:160]
 
 
 
 
 
 
 
 
173
  # 3. a parenthetical right after the spouse's name, REQUIRING a marriage marker — "(1848–1919)" is a lifespan
174
- MARK = {"en": r"m\.|marr", "de": r"⚭|verh", "fr": r"mari|ép", "es": r"matr|casad", "it": r"spos", "ru": r"брак|с\s", "uk": r"шлюб|з\s", "pl": r"ślub|od\s", "pt": r"casad", "nl": r"getr", "sv": r"gift", "da": r"gift", "fi": r"avio", "cs": r"sňat|od\s", "hu": r"házas", "tr": r"evl", "fa": r"ازدواج", "ar": r"تزوج|زواج", "ja": r"結婚", "zh": r"结婚|結婚", "hy": r"ամուսն"}
175
- mark = MARK.get(lang, r"⚭|m\.")
176
  m = re.search(name_re + r"[^\n(]{0,40}\((?:[^)]{0,40}?(?:" + mark + r")[^)]{0,40}?)" + YEAR + r"[^)]*\)", text, re.I)
177
  if m:
178
  yr = re.search(YEAR, m.group(0))
179
- return yr.group(0) + "-00-00", 9, m.group(0)[:160]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
  return None
181
 
182
 
@@ -217,7 +295,7 @@ def harvest(langs=None):
217
  continue
218
  hit = _find_dates(lang, text, names)
219
  if hit:
220
- st, prec, snip = hit; sy = int(st[:4])
221
  by = [int(r[k][:4]) for k in ("adob", "bdob") if r[k][:4].isdigit()]
222
  ey = [int(r[k][:4]) for k in ("end", "adeath", "bdeath") if r[k][:4].isdigit()]
223
  # a wedding before either 14th birthday, or after the marriage's own end, is a misread — dropped
@@ -225,7 +303,7 @@ def harvest(langs=None):
225
  continue
226
  if ey and sy > min(ey):
227
  continue
228
- w.writerow([r["a"], r["b"], st, prec, lang, subj, snip.replace("\n", " ")]); got += 1
229
  if i % 400 == 0 and i:
230
  f.flush(); log(f" {lang}: {i:,}/{len(jobs):,} read · {got:,} dates found")
231
  time.sleep(0.6)
 
139
 
140
 
141
  # ── phase C: the wikitext harvest ────────────────────────────────────────────────────────────────────────────────
142
+ MONTHS = {
143
+ "en": "january february march april may june july august september october november december",
144
+ "de": "januar februar märz april mai juni juli august september oktober november dezember",
145
+ "fr": "janvier février mars avril mai juin juillet août septembre octobre novembre décembre",
146
+ "es": "enero febrero marzo abril mayo junio julio agosto septiembre octubre noviembre diciembre",
147
+ "it": "gennaio febbraio marzo aprile maggio giugno luglio agosto settembre ottobre novembre dicembre",
148
+ "pt": "janeiro fevereiro março abril maio junho julho agosto setembro outubro novembro dezembro",
149
+ "nl": "januari februari maart april mei juni juli augustus september oktober november december",
150
+ "sv": "januari februari mars april maj juni juli augusti september oktober november december",
151
+ "da": "januar februar marts april maj juni juli august september oktober november december",
152
+ "fi": "tammikuuta helmikuuta maaliskuuta huhtikuuta toukokuuta kesäkuuta heinäkuuta elokuuta syyskuuta lokakuuta marraskuuta joulukuuta",
153
+ "pl": "stycznia lutego marca kwietnia maja czerwca lipca sierpnia września października listopada grudnia",
154
+ "cs": "ledna února března dubna května června července srpna září října listopadu prosince",
155
+ "ru": "января февраля марта апреля мая июня июля августа сентября октября ноября декабря",
156
+ "uk": "січня лютого березня квітня травня червня липня серпня вересня жовтня листопада грудня",
157
+ }
158
+ _MTAB = {lang: {m: i + 1 for i, m in enumerate(v.split())} for lang, v in MONTHS.items()}
159
+
160
+
161
+ def _full_date(lang, snippet):
162
+ """A day-precision ISO date inside the snippet, or None: ISO, '11 March 1902' (15 languages' month names,
163
+ prefix-matched), 'March 11, 1902', ja/zh 1902年3月11日."""
164
+ iso = re.search(YEAR + r"-(\d\d)-(\d\d)", snippet)
165
+ if iso:
166
+ return iso.group(0)
167
+ m = re.search(YEAR + r"年(\d{1,2})月(\d{1,2})日", snippet)
168
+ if m:
169
+ return f"{m.group(1)}-{int(m.group(2)):02d}-{int(m.group(3)):02d}"
170
+ tab = _MTAB.get(lang, {}); tab_en = _MTAB["en"]
171
+ m = re.search(r"(\d{1,2})\.?\s+([^\W\d_]+),?\s+" + YEAR, snippet, re.U) or re.search(r"([^\W\d_]+)\s+(\d{1,2}),?\s+" + YEAR, snippet, re.U)
172
+ if m:
173
+ g = m.groups(); word = (g[1] if g[0].isdigit() else g[0]).lower(); day = int(g[0] if g[0].isdigit() else g[1]); yr = g[2]
174
+ for t in (tab, tab_en):
175
+ for name, mon in t.items():
176
+ if word.startswith(name[:4]) and 1 <= day <= 31:
177
+ return f"{yr}-{mon:02d}-{day:02d}"
178
+ return None
179
+
180
+
181
+ def _end_year(snippet, start_year):
182
+ """The marriage's END year inside the same construct: any later year in the snippet (a {{marriage}} second
183
+ argument, 'div. 1911', 'ended 1911'); None when the construct names none."""
184
+ ys = [int(y) for y in re.findall(YEAR, snippet)]
185
+ later = [y for y in ys if y > start_year]
186
+ return min(later) if later else None
187
+
188
+
189
  def _find_dates(lang, text, spouse_names):
190
+ """(start_iso, prec, snippet, end_year) for the marriage to this spouse, or None. prec 11 day / 9 year."""
191
  esc = [re.escape(n) for n in spouse_names if n and len(n) > 2]
192
  if not esc:
193
  return None
 
198
  body = m.group(1)
199
  if not re.search(name_re, body, re.I):
200
  continue
201
+ fd = _full_date(lang, body)
202
+ if fd:
203
+ return fd, 11, body[:160], _end_year(body, int(fd[:4]))
 
 
 
 
 
 
204
  yr = re.search(YEAR, body)
205
  if yr:
206
+ return yr.group(0) + "-00-00", 9, body[:160], _end_year(body, int(yr.group(0)))
207
  # 2. the spouse parameter's value line: |spouse = [[Name]] (m. 1903; div 1919) / ⚭ 1903 / с 1903
208
  for par in SPOUSE_PARAMS.get(lang, []) + SPOUSE_PARAMS.get("en", []):
209
  for m in re.finditer(r"\|\s*" + re.escape(par) + r"\s*=([^\n]*?" + name_re + r"[^\n]*)", text, re.I):
210
  line = m.group(1)
211
+ # a line can list several spouses -- "A (1890-1900); B (1902-1911)" -- the year must come from OUR
212
+ # spouse's SEGMENT, never the first year in the line
213
+ for seg in re.split(r"(?<=\))\s*;|<br\s*/?>|\}\}\s*\{\{", line): # a spouse separator is ");" semicolons INSIDE a parenthetical stay
214
+ if not re.search(name_re, seg, re.I):
215
+ continue
216
+ fd = _full_date(lang, seg)
217
+ if fd:
218
+ return fd, 11, seg[:160], _end_year(seg, int(fd[:4]))
219
+ yr = re.search(YEAR, seg)
220
+ if yr:
221
+ return yr.group(0) + "-00-00", 9, seg[:160], _end_year(seg, int(yr.group(0)))
222
  # 3. a parenthetical right after the spouse's name, REQUIRING a marriage marker — "(1848–1919)" is a lifespan
223
+ MARK = {"en": r"m\.|marr", "de": r"⚭|∞|verh", "fr": r"mari|ép", "es": r"matr|casad", "it": r"spos", "ru": r"брак|с\s", "uk": r"шлюб|з\s", "pl": r"ślub|od\s", "pt": r"casad", "nl": r"getr", "sv": r"gift", "da": r"gift", "fi": r"avio", "cs": r"sňat|od\s", "hu": r"házas", "tr": r"evl", "fa": r"ازدواج", "ar": r"تزوج|زواج", "ja": r"結婚", "zh": r"结婚|結婚", "hy": r"ամուսն"}
224
+ mark = MARK.get(lang, r"⚭|∞|m\.")
225
  m = re.search(name_re + r"[^\n(]{0,40}\((?:[^)]{0,40}?(?:" + mark + r")[^)]{0,40}?)" + YEAR + r"[^)]*\)", text, re.I)
226
  if m:
227
  yr = re.search(YEAR, m.group(0))
228
+ return yr.group(0) + "-00-00", 9, m.group(0)[:160], _end_year(m.group(0), int(yr.group(0)))
229
+ # 3b. the genealogy signs need no parenthetical: "[[Alma]] ⚭ 1902" / "∞ 1902 [[Alma]]"
230
+ for pat in (name_re + r"[^\n]{0,15}[⚭∞][^\n]{0,15}" + YEAR, r"[⚭∞][^\n]{0,15}" + YEAR + r"[^\n]{0,25}" + name_re, YEAR + r"[^\n]{0,15}[⚭∞][^\n]{0,25}" + name_re):
231
+ m = re.search(pat, text, re.I)
232
+ if m:
233
+ yr = re.search(YEAR, m.group(0))
234
+ return yr.group(0) + "-00-00", 9, m.group(0)[:160], _end_year(m.group(0), int(yr.group(0)))
235
+ # 4. PROSE (the wikis that keep marriage in running text — dewiki rejects person infoboxes altogether):
236
+ # "heiratete [am 9. März] 1902 [[Alma]]" / "épousa [[Alma]] en 1902" / "женился на [[Альме]] в 1902" —
237
+ # a marriage VERB, the spouse and a year within one clause, either order; the plausibility gate downstream
238
+ # still rejects any year that cannot be a wedding
239
+ VERB = {"de": r"heiratete|ehelichte|verm[äa]hlte|schloss.{0,20}Ehe", "fr": r"épous[ae]|se mari[ae]|mariage avec", "es": r"se cas[óo]|contrajo matrimonio",
240
+ "it": r"spos[òo]", "pt": r"casou(?:-se)?", "ru": r"женился|вышла замуж|вступил[аи]? в брак|обвенчал", "uk": r"одружився|вийшла заміж",
241
+ "pl": r"poślubił|ożenił|wyszła za", "nl": r"trouwde|huwde", "sv": r"gifte sig", "da": r"giftede sig", "fi": r"avioitui|meni naimisiin",
242
+ "cs": r"oženil se|vdala se|vzal si", "hu": r"feleségül vette|házasságot kötött", "tr": r"evlendi", "ja": r"結婚", "zh": r"结婚|結婚",
243
+ "fa": r"ازدواج کرد", "ar": r"تزوج", "hy": r"ամուսնացավ|ամուսնացել", "en": r"married"}
244
+ verb = VERB.get(lang)
245
+ if verb:
246
+ win = r"[^\n;]{0,90}" # periods allowed: German ordinal dates ("9. März 1902") sit inside the clause
247
+ V = r"(?:" + verb + r")"
248
+ for pat in (V + win + name_re + win + YEAR, V + win + YEAR + win + name_re, name_re + win + V + win + YEAR,
249
+ YEAR + win + V + win + name_re, name_re + win + YEAR + win + V, YEAR + win + name_re + win + V): # all six orders
250
+ m = re.search(pat, text, re.I)
251
+ if m:
252
+ clause = text[max(0, m.start() - 24):m.end()] # a day often precedes the matched year ("9 марта 1902 ...")
253
+ fd = _full_date(lang, clause)
254
+ if fd:
255
+ return fd, 11, clause[:160], _end_year(clause, int(fd[:4]))
256
+ yr = re.search(YEAR, m.group(0))
257
+ return yr.group(0) + "-00-00", 9, m.group(0)[:160], _end_year(m.group(0), int(yr.group(0)))
258
  return None
259
 
260
 
 
295
  continue
296
  hit = _find_dates(lang, text, names)
297
  if hit:
298
+ st, prec, snip, endy = hit; sy = int(st[:4])
299
  by = [int(r[k][:4]) for k in ("adob", "bdob") if r[k][:4].isdigit()]
300
  ey = [int(r[k][:4]) for k in ("end", "adeath", "bdeath") if r[k][:4].isdigit()]
301
  # a wedding before either 14th birthday, or after the marriage's own end, is a misread — dropped
 
303
  continue
304
  if ey and sy > min(ey):
305
  continue
306
+ w.writerow([r["a"], r["b"], st, prec, lang, subj, snip.replace("\n", " "), endy if endy is not None else ""]); got += 1
307
  if i % 400 == 0 and i:
308
  f.flush(); log(f" {lang}: {i:,}/{len(jobs):,} read · {got:,} dates found")
309
  time.sleep(0.6)