Your problem is that [^<]*
matches anything except an opening angle bracket. That's good idea in general, so you don't accidentally match across tag boundaries, but in this case it's unfortunate because there's a <p>
tag right after the <td>
.
Therefore, I propose a different solution. Allow other tags, just not <td>
tags within a <td>
tag:
<tr>
<td>((?:(?!</?td)[\s\S])*)</td>
<td>((?:(?!</?td)[\s\S])*)</td>
<td>((?:(?!</?td)[\s\S])*)</td>
</tr>
Explanation:
(?: # Start non-capturing group that matches...
(?!</?td) # (unless we're at the start of a <td> or </td> tag)
[\s\S] # ... any character (whitespace or non-whitespace).
)* # Repeat as needed